From 72fedc5d1f88f6783f4b9da50e2ae89fc1956763 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 28 Jul 2010 12:53:23 +0400 Subject: [PATCH] new api for file processing compilers --- .../intellij/compiler/impl/CompileDriver.java | 13 +- .../compiler/impl/CompilerCacheManager.java | 22 ++ .../compiler/impl/NewCompilerRunner.java | 246 ++++++++++++++++++ .../compiler/impl/newApi/BuildTarget.java | 39 +++ .../compiler/impl/newApi/CompileItem.java | 31 +++ .../impl/newApi/CompilerInstance.java | 60 +++++ .../compiler/impl/newApi/NewCompiler.java | 62 +++++ .../impl/newApi/NewCompilerCache.java | 132 ++++++++++ .../newApi/NewCompilerPersistentData.java | 119 +++++++++ .../newApi/SingleTargetCompilerInstance.java | 48 ++++ .../impl/newApi/VirtualFileCompileItem.java | 55 ++++ .../newApi/VirtualFilePersistentState.java | 31 +++ .../newApi/VirtualFileStateExternalizer.java | 44 ++++ 13 files changed, 898 insertions(+), 4 deletions(-) create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java create mode 100644 java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 230c9e999ec9..5a29390a9e1d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -24,6 +24,7 @@ package com.intellij.compiler.impl; import com.intellij.CommonBundle; import com.intellij.analysis.AnalysisScope; import com.intellij.compiler.*; +import com.intellij.compiler.impl.newApi.NewCompiler; import com.intellij.compiler.make.CacheCorruptedException; import com.intellij.compiler.make.CacheUtils; import com.intellij.compiler.make.DependencyCache; @@ -560,7 +561,7 @@ public class CompileDriver { return CompilerBundle.message("status.compilation.completed.successfully.with.warnings.and.errors", errorCount, warningCount); } - private static class ExitStatus { + static class ExitStatus { private final String myName; private ExitStatus(@NonNls String name) { @@ -577,10 +578,10 @@ public class CompileDriver { public static final ExitStatus UP_TO_DATE = new ExitStatus("UP_TO_DATE"); } - private static class ExitException extends Exception { + static class ExitException extends Exception { private final ExitStatus myStatus; - private ExitException(ExitStatus status) { + ExitException(ExitStatus status) { myStatus = status; } @@ -724,7 +725,7 @@ public class CompileDriver { boolean didSomething = false; final CompilerManager compilerManager = CompilerManager.getInstance(myProject); - + NewCompilerRunner runner = new NewCompilerRunner(context, compilerManager, forceCompile, onlyCheckStatus); try { didSomething |= generateSources(compilerManager, context, forceCompile, onlyCheckStatus); @@ -746,19 +747,23 @@ public class CompileDriver { didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassInstrumentingCompiler.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.CLASS_INSTRUMENTING); // explicitly passing forceCompile = false because in scopes that is narrower than ProjectScope it is impossible // to understand whether the class to be processed is in scope or not. Otherwise compiler may process its items even if // there were changes in completely independent files. didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassPostProcessingCompiler.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.CLASS_POST_PROCESSING); didSomething |= invokeFileProcessingCompilers(compilerManager, context, PackagingCompiler.class, FILE_PACKAGING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.PACKAGING); didSomething |= invokeFileProcessingCompilers(compilerManager, context, Validator.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, forceCompile, true, onlyCheckStatus); + didSomething |= runner.invokeCompilers(NewCompiler.CompileOrderPlace.VALIDATING); } catch (ExitException e) { if (LOG.isDebugEnabled()) { diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java index c99dc6f64924..76082f3fbce2 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerCacheManager.java @@ -15,6 +15,8 @@ */ package com.intellij.compiler.impl; +import com.intellij.compiler.impl.newApi.NewCompiler; +import com.intellij.compiler.impl.newApi.NewCompilerCache; import com.intellij.openapi.Disposable; import com.intellij.openapi.compiler.*; import com.intellij.openapi.compiler.Compiler; @@ -42,6 +44,7 @@ import java.util.Map; public class CompilerCacheManager implements ProjectComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompilerCacheManager"); private final Map myCompilerToCacheMap = new HashMap(); + private final Map, NewCompilerCache> myNewCachesMap = new HashMap, NewCompilerCache>(); private final List myCacheDisposables = new ArrayList(); private final File myCachesRoot; private final Runnable myShutdownTask = new Runnable() { @@ -49,8 +52,10 @@ public class CompilerCacheManager implements ProjectComponent { flushCaches(); } }; + private final Project myProject; public CompilerCacheManager(Project project) { + myProject = project; myCachesRoot = CompilerPaths.getCacheStoreDirectory(project); } @@ -85,6 +90,23 @@ public class CompilerCacheManager implements ProjectComponent { return dir; } + public synchronized NewCompilerCache getNewCompilerCache(NewCompiler compiler) throws IOException { + NewCompilerCache cache = myNewCachesMap.get(compiler); + if (cache == null) { + final NewCompilerCache newCache = new NewCompilerCache(compiler, NewCompilerRunner.getNewCompilerCacheDir(myProject, compiler)); + myNewCachesMap.put(compiler, newCache); + myCacheDisposables.add(new Disposable() { + @Override + public void dispose() { + newCache.close(); + } + }); + cache = newCache; + } + //noinspection unchecked + return (NewCompilerCache)cache; + } + public synchronized FileProcessingCompilerStateCache getFileProcessingCompilerCache(FileProcessingCompiler compiler) throws IOException { Object cache = myCompilerToCacheMap.get(compiler); if (cache == null) { diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java b/java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java new file mode 100644 index 000000000000..a5dc5e2ffc24 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/NewCompilerRunner.java @@ -0,0 +1,246 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl; + +import com.intellij.compiler.impl.newApi.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.compiler.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.util.CommonProcessors; +import com.intellij.util.Processor; +import com.intellij.util.io.KeyDescriptor; +import gnu.trove.THashSet; +import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * @author nik + */ +public class NewCompilerRunner { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.NewCompilerRunner"); + private CompileContext myContext; + private final boolean myForceCompile; + private final boolean myOnlyCheckStatus; + private final NewCompiler[] myCompilers; + private final Project myProject; + + public NewCompilerRunner(CompileContext context, CompilerManager compilerManager, boolean forceCompile, boolean onlyCheckStatus) { + myContext = context; + myForceCompile = forceCompile; + myOnlyCheckStatus = onlyCheckStatus; + myCompilers = compilerManager.getCompilers(NewCompiler.class); + myProject = myContext.getProject(); + } + + public boolean invokeCompilers(NewCompiler.CompileOrderPlace place) throws CompileDriver.ExitException { + boolean didSomething = false; + try { + for (NewCompiler compiler : myCompilers) { + if (compiler.getOrderPlace().equals(place)) { + didSomething = invokeCompiler(compiler); + } + } + } + catch (IOException e) { + LOG.info(e); + myContext.requestRebuildNextTime(e.getMessage()); + throw new CompileDriver.ExitException(CompileDriver.ExitStatus.ERRORS); + } + catch (CompileDriver.ExitException e) { + throw e; + } + catch (ProcessCanceledException e) { + throw e; + } + catch (Exception e) { + LOG.info(e); + myContext.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.exception", e.getMessage()), null, -1, -1); + } + return didSomething; + } + + private boolean invokeCompiler(NewCompiler compiler) throws IOException, CompileDriver.ExitException { + return invokeCompiler(compiler, compiler.createInstance(myContext)); + } + + private , Key, State> + boolean invokeCompiler(NewCompiler compiler, CompilerInstance instance) throws IOException, CompileDriver.ExitException { + NewCompilerCache cache = CompilerCacheManager.getInstance(myProject).getNewCompilerCache(compiler); + NewCompilerPersistentData data = new NewCompilerPersistentData(getNewCompilerCacheDir(myProject, compiler), compiler.getVersion()); + if (data.isVersionChanged()) { + LOG.info("Clearing cache for " + compiler.getDescription()); + cache.wipe(); + } + + Set targetsToRemove = new HashSet(data.getAllTargets()); + for (T target : instance.getAllTargets()) { + targetsToRemove.remove(target.getId()); + } + if (!myOnlyCheckStatus) { + for (String target : targetsToRemove) { + int id = data.removeId(target); + if (LOG.isDebugEnabled()) { + LOG.debug("Removing obsolete target '" + target + "' (id=" + id + ")"); + } + List keys = new ArrayList(); + cache.processSources(id, new CommonProcessors.CollectProcessor(keys)); + List> obsoleteSources = new ArrayList>(); + for (Key key : keys) { + final State state = cache.getState(id, key); + obsoleteSources.add(Pair.create(key, state)); + } + instance.processObsoleteTarget(target, obsoleteSources); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) { + return true; + } + for (Key key : keys) { + cache.remove(id, key); + } + } + } + + boolean didSomething = false; + for (T target : instance.getSelectedTargets()) { + int id = data.getId(target.getId()); + didSomething |= processTarget(target, id, compiler, instance, cache); + } + + data.save(); + return didSomething; + } + + public static File getNewCompilerCacheDir(Project project, NewCompiler compiler) { + return new File(CompilerPaths.getCacheStoreDirectory(project), compiler.getId()); + } + + private , Key, State> + boolean processTarget(T target, final int targetId, final NewCompiler compiler, final CompilerInstance instance, + final NewCompilerCache cache) throws IOException, CompileDriver.ExitException { + if (LOG.isDebugEnabled()) { + LOG.debug("Processing target '" + target + "' (id=" + targetId + ")"); + } + final List items = instance.getItems(target); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) return true; + + final List> toProcess = new ArrayList>(); + final THashSet keySet = new THashSet(new SourceItemHashingStrategy(compiler)); + final Ref exception = Ref.create(null); + DumbService.getInstance(myProject).waitForSmartMode(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + try { + for (Item item : items) { + final Key key = item.getKey(); + keySet.add(key); + State output = cache.getState(targetId, key); + if (myForceCompile || output == null || !item.isUpToDate(output)) { + toProcess.add(Pair.create(item, output)); + } + } + } + catch (IOException e) { + exception.set(e); + } + } + }); + if (!exception.isNull()) { + throw exception.get(); + } + + final List toRemove = new ArrayList(); + cache.processSources(targetId, new Processor() { + @Override + public boolean process(Key key) { + if (!keySet.contains(key)) { + toRemove.add(key); + } + return true; + } + }); + + if (LOG.isDebugEnabled()) { + LOG.debug(toProcess.size() + " items will be processed, " + toRemove.size() + " items will be removed"); + } + + if (toProcess.isEmpty() && toRemove.isEmpty()) { + return false; + } + + if (myOnlyCheckStatus) { + throw new CompileDriver.ExitException(CompileDriver.ExitStatus.CANCELLED); + } + + List> obsoleteItems = new ArrayList>(); + for (Key key : toRemove) { + obsoleteItems.add(Pair.create(key, cache.getState(targetId, key))); + } + + final List processedItems = new ArrayList(); + final List toRefresh = new ArrayList(); + instance.processItems(target, toProcess, obsoleteItems, new CompilerInstance.OutputConsumer() { + @Override + public void addFileToRefresh(@NotNull File file) { + toRefresh.add(file); + } + @Override + public void addProcessedItem(@NotNull Item sourceItem) { + processedItems.add(sourceItem); + } + }); + if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) { + return true; + } + + for (Key key : toRemove) { + cache.remove(targetId, key); + } + CompilerUtil.refreshIOFiles(toRefresh); + for (Item item : processedItems) { + cache.putOutput(targetId, item.getKey(), item.computeState()); + } + + return true; + + } + + private class SourceItemHashingStrategy implements TObjectHashingStrategy { + private KeyDescriptor myKeyDescriptor; + + public SourceItemHashingStrategy(NewCompiler compiler) { + myKeyDescriptor = compiler.getItemKeyDescriptor(); + } + + @Override + public int computeHashCode(S object) { + return myKeyDescriptor.getHashCode(object); + } + + @Override + public boolean equals(S o1, S o2) { + return myKeyDescriptor.isEqual(o1, o2); + } + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java new file mode 100644 index 000000000000..a53ffc1ff9c0 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/BuildTarget.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class BuildTarget { + public static final BuildTarget DEFAULT = new BuildTarget() { + @NotNull + @Override + public String getId() { + return ""; + } + }; + + @NotNull + public abstract String getId(); + + @Override + public String toString() { + return "Build Target: " + getId(); + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java new file mode 100644 index 000000000000..fb941ca3e4b2 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompileItem.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class CompileItem { + @NotNull + public abstract Key getKey(); + + public abstract boolean isUpToDate(@NotNull State state); + + @NotNull + public abstract State computeState(); +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java new file mode 100644 index 000000000000..2e9f9cf59593 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/CompilerInstance.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +/** + * @author nik + */ +public abstract class CompilerInstance, Key, State> { + protected final CompileContext myContext; + + protected CompilerInstance(CompileContext context) { + myContext = context; + } + + protected Project getProject() { + return myContext.getProject(); + } + + @NotNull + public abstract List getAllTargets(); + + @NotNull + public abstract List getSelectedTargets(); + + public abstract void processObsoleteTarget(@NotNull String targetId, @NotNull List> obsoleteItems); + + + @NotNull + public abstract List getItems(@NotNull T target); + + public abstract void processItems(@NotNull T target, @NotNull List> changedItems, @NotNull List> obsoleteItems, + @NotNull OutputConsumer consumer); + + public interface OutputConsumer> { + void addFileToRefresh(@NotNull File file); + + void addProcessedItem(@NotNull Item sourceItem); + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java new file mode 100644 index 000000000000..b82b8bf9d84c --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompiler.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.Compiler; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.KeyDescriptor; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class NewCompiler implements Compiler { + private final String myId; + private final int myVersion; + private final CompileOrderPlace myOrderPlace; + + protected NewCompiler(@NotNull String id, int version, @NotNull CompileOrderPlace orderPlace) { + myId = id; + myVersion = version; + myOrderPlace = orderPlace; + } + + @NotNull + public abstract KeyDescriptor getItemKeyDescriptor(); + @NotNull + public abstract DataExternalizer getItemStateExternalizer(); + + @NotNull + public abstract CompilerInstance, Key, State> createInstance(@NotNull CompileContext context); + + public final String getId() { + return myId; + } + + public final int getVersion() { + return myVersion; + } + + public CompileOrderPlace getOrderPlace() { + return myOrderPlace; + } + + public static enum CompileOrderPlace { + CLASS_INSTRUMENTING, CLASS_POST_PROCESSING, PACKAGING, VALIDATING + } + +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java new file mode 100644 index 000000000000..0b6ea2ecc3e0 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerCache.java @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.Processor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.PersistentHashMap; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; + +/** + * @author nik + */ +public class NewCompilerCache { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.newApi.NewCompilerCache"); + private PersistentHashMap, State> myPersistentMap; + private File myCacheFile; + private final NewCompiler myCompiler; + + public NewCompilerCache(NewCompiler compiler, final File compilerCacheDir) throws IOException { + myCompiler = compiler; + myCacheFile = new File(compilerCacheDir, "timestamps"); + createMap(); + } + + private void createMap() throws IOException { + myPersistentMap = new PersistentHashMap, State>(myCacheFile, new SourceItemDataDescriptor(myCompiler.getItemKeyDescriptor()), + myCompiler.getItemStateExternalizer()); + } + + private KeyAndTargetData getKeyAndTargetData(Key key, int target) { + KeyAndTargetData data = new KeyAndTargetData(); + data.myTarget = target; + data.myKey = key; + return data; + } + + public void wipe() throws IOException { + try { + myPersistentMap.close(); + } + catch (IOException ignored) { + } + PersistentHashMap.deleteFilesStartingWith(myCacheFile); + createMap(); + } + + public void close() { + try { + myPersistentMap.close(); + } + catch (IOException e) { + LOG.info(e); + } + } + + public void remove(int targetId, Key key) throws IOException { + myPersistentMap.remove(getKeyAndTargetData(key, targetId)); + } + + public State getState(int targetId, Key key) throws IOException { + return myPersistentMap.get(getKeyAndTargetData(key, targetId)); + } + + public void processSources(final int targetId, final Processor processor) throws IOException { + myPersistentMap.processKeys(new Processor>() { + @Override + public boolean process(KeyAndTargetData data) { + return targetId == data.myTarget ? processor.process(data.myKey) : true; + } + }); + } + + public void putOutput(int targetId, Key key, State outputItem) throws IOException { + myPersistentMap.put(getKeyAndTargetData(key, targetId), outputItem); + } + + + private static class KeyAndTargetData { + public int myTarget; + public Key myKey; + } + + private class SourceItemDataDescriptor implements KeyDescriptor> { + private final KeyDescriptor myKeyDescriptor; + + public SourceItemDataDescriptor(KeyDescriptor keyDescriptor) { + myKeyDescriptor = keyDescriptor; + } + + @Override + public boolean isEqual(KeyAndTargetData val1, KeyAndTargetData val2) { + return val1.myTarget == val2.myTarget; + } + + @Override + public int getHashCode(KeyAndTargetData value) { + return value.myTarget + 239 * myKeyDescriptor.getHashCode(value.myKey); + } + + @Override + public void save(DataOutput out, KeyAndTargetData value) throws IOException { + out.writeInt(value.myTarget); + myKeyDescriptor.save(out, value.myKey); + } + + + @Override + public KeyAndTargetData read(DataInput in) throws IOException { + int target = in.readInt(); + final Key item = myKeyDescriptor.read(in); + return getKeyAndTargetData(item, target); + } + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java new file mode 100644 index 000000000000..1746c334cf77 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/NewCompilerPersistentData.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.io.IOUtil; +import gnu.trove.TIntHashSet; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author nik + */ +public class NewCompilerPersistentData { + private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.newApi.NewCompilerPersistentData"); + private static final int VERSION = 0; + private File myFile; + private Map myTarget2Id = new HashMap(); + private TIntHashSet myUsedIds = new TIntHashSet(); + private boolean myVersionChanged; + private final int myCompilerVersion; + + public NewCompilerPersistentData(File cacheStoreDirectory, int compilerVersion) throws IOException { + myCompilerVersion = compilerVersion; + myFile = new File(cacheStoreDirectory, "info"); + if (!myFile.exists()) { + LOG.info("Compiler info file doesn't exists: " + myFile.getAbsolutePath()); + myVersionChanged = true; + return; + } + + DataInputStream input = new DataInputStream(new FileInputStream(myFile)); + try { + final int dataVersion = input.readInt(); + if (dataVersion != VERSION) { + LOG.info("Version of compiler info file (" + myFile.getAbsolutePath() + ") changed: " + dataVersion + " -> " + VERSION); + myVersionChanged = true; + return; + } + + final int savedCompilerVersion = input.readInt(); + if (savedCompilerVersion != compilerVersion) { + LOG.info("Compiler caches version changed (" + myFile.getAbsolutePath() + "): " + savedCompilerVersion + " -> " + compilerVersion); + myVersionChanged = true; + return; + } + + int size = input.readInt(); + while (size-- > 0) { + final String target = IOUtil.readString(input); + final int id = input.readInt(); + myTarget2Id.put(target, id); + myUsedIds.add(id); + } + } + finally { + input.close(); + } + } + + public boolean isVersionChanged() { + return myVersionChanged; + } + + public void save() throws IOException { + final DataOutputStream output = new DataOutputStream(new FileOutputStream(myFile)); + try { + output.writeInt(VERSION); + output.writeInt(myCompilerVersion); + output.writeInt(myTarget2Id.size()); + + for (Map.Entry entry : myTarget2Id.entrySet()) { + IOUtil.writeString(entry.getKey(), output); + output.writeInt(entry.getValue()); + } + } + finally { + output.close(); + } + } + + public int getId(@NotNull String target) { + if (myTarget2Id.containsKey(target)) { + return myTarget2Id.get(target); + } + int id = 0; + while (myUsedIds.contains(id)) { + id++; + } + myTarget2Id.put(target, id); + myUsedIds.add(id); + return id; + } + + public Set getAllTargets() { + return myTarget2Id.keySet(); + } + + public int removeId(String target) { + return myTarget2Id.remove(target); + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java new file mode 100644 index 000000000000..359366e70bb4 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/SingleTargetCompilerInstance.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public abstract class SingleTargetCompilerInstance, S, O> extends CompilerInstance { + protected SingleTargetCompilerInstance(CompileContext context) { + super(context); + } + + @NotNull + @Override + public List getAllTargets() { + return Collections.singletonList(BuildTarget.DEFAULT); + } + + @NotNull + @Override + public List getSelectedTargets() { + return getAllTargets(); + } + + @Override + public void processObsoleteTarget(@NotNull String targetId, @NotNull List> obsoleteItems) { + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java new file mode 100644 index 000000000000..a2c0b7055c9f --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileCompileItem.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public abstract class VirtualFileCompileItem extends CompileItem { + public static final KeyDescriptor KEY_DESCRIPTOR = new EnumeratorStringDescriptor(); + protected final VirtualFile myFile; + + public VirtualFileCompileItem(@NotNull VirtualFile file) { + myFile = file; + } + + @NotNull + public VirtualFile getFile() { + return myFile; + } + + @Override + public final boolean isUpToDate(@NotNull State state) { + if (myFile.getTimeStamp() != state.getSourceTimestamp()) { + return false; + } + return isStateUpToDate(state); + } + + protected abstract boolean isStateUpToDate(State state); + + @NotNull + @Override + public String getKey() { + return myFile.getUrl(); + } + +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java new file mode 100644 index 000000000000..9f30a9182e99 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFilePersistentState.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +/** +* @author nik +*/ +public class VirtualFilePersistentState { + private final long mySourceTimestamp; + + public VirtualFilePersistentState(long sourceTimestamp) { + mySourceTimestamp = sourceTimestamp; + } + + public final long getSourceTimestamp() { + return mySourceTimestamp; + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java new file mode 100644 index 000000000000..6ca460108979 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/newApi/VirtualFileStateExternalizer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.compiler.impl.newApi; + +import com.intellij.util.io.DataExternalizer; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** +* @author nik +*/ +public abstract class VirtualFileStateExternalizer implements DataExternalizer { + protected abstract void doSave(DataOutput out, State value) throws IOException; + + protected abstract State doRead(DataInput in, long sourceTimestamp) throws IOException; + + @Override + public final void save(DataOutput out, State value) throws IOException { + out.writeLong(value.getSourceTimestamp()); + doSave(out, value); + } + + @Override + public final State read(DataInput in) throws IOException { + final long sourceTimestamp = in.readLong(); + return doRead(in, sourceTimestamp); + } + +}