incremental compilation: ensure javac always resolves dependencies within same module against sources and not classes compiled on previous steps (IDEA-108215)

This commit is contained in:
Eugene Zhuravlev
2013-06-14 18:04:05 +04:00
parent e1ce52e3bf
commit 7cd43429a7
15 changed files with 109 additions and 13 deletions
@@ -0,0 +1,10 @@
Cleaning output files:
out/production/RecompileTwinDependencies/package1/A.class
End of files
Cleaning output files:
out/production/RecompileTwinDependencies/package1/C.class
End of files
Compiling files:
src/package2/A.java
src/package2/C.java
End of files
@@ -0,0 +1,10 @@
package com;
import package1.*;
import package2.*;
public class B {
public A get() { // resolves to "public package1.A get();" or "public package2.A get();" depending on where A is
return null;
}
}
@@ -0,0 +1,4 @@
package package1;
public class A { // resolves to "class package1.A"
}
@@ -0,0 +1,9 @@
package package1;
import com.B;
public class C {
{
new B().get(); // resolves to invoking of "com/B.get:()Lpackage1/A;"
}
}
@@ -0,0 +1,5 @@
package package1;
// Dummy class for non-empty package
public class Dummy {
}
@@ -0,0 +1,4 @@
package package2;
public class A { // resolves to "class package2.A"
}
@@ -0,0 +1,11 @@
package package2;
import com.B;
public class C {
{
new B().get(); // should be resolved to "com/B.get:()Lpackage2/A;"
// but package2/C.java is first compiled when B.class still contains "public package1.A get();"
// and compiler returns an error
}
}
@@ -0,0 +1,5 @@
package package2;
// Dummy class for non-empty package
public class Dummy {
}
@@ -935,7 +935,21 @@ public class IncProjectBuilder {
final SourceToOutputMapping sourceToOutputStorage = context.getProjectDescriptor().dataManager.getSourceToOutputMap(target);
final ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger();
// actually delete outputs associated with removed paths
for (String deletedSource : deletedPaths) {
final Collection<String> pathsForIteration;
if (Utils.IS_TEST_MODE) {
// ensure predictable order in test logs
pathsForIteration = new ArrayList<String>(deletedPaths);
Collections.sort((List<String>)pathsForIteration, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
}
else {
pathsForIteration = deletedPaths;
}
for (String deletedSource : pathsForIteration) {
// deleting outputs corresponding to non-existing source
final Collection<String> outputs = sourceToOutputStorage.getOutputs(deletedSource);
@@ -72,15 +72,24 @@ public class BuildFSState extends FSState {
return super.getSourcesToRecompile(context, target);
}
//public boolean isMarkedForRecompilation(BuildRootDescriptor rd, File file) {
// final Map<BuildRootDescriptor, Set<File>> recompile = getDelta(rd.getTarget()).getSourcesToRecompile();
// //noinspection SynchronizationOnLocalVariableOrMethodParameter
// synchronized (recompile) {
// final Set<File> files = recompile.get(rd);
// return files != null && files.contains(file);
// }
//}
public boolean isMarkedForRecompilation(@Nullable CompileContext context, BuildRootDescriptor rd, File file) {
FilesDelta delta = getRoundDelta(LAST_ROUND_DELTA_KEY, context);
if (delta == null) {
delta = getDelta(rd.getTarget());
}
final Map<BuildRootDescriptor, Set<File>> recompile = delta.getSourcesToRecompile();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (recompile) {
final Set<File> files = recompile.get(rd);
return files != null && files.contains(file);
}
}
/**
* Note: marked file will well be visible as "dirty" only on the next compilation round!
* @throws IOException
*/
@Override
public boolean markDirty(@Nullable CompileContext context, File file, final BuildRootDescriptor rd, @Nullable Timestamps tsStorage, boolean saveEventStamp) throws IOException {
final FilesDelta roundDelta = getRoundDelta(CURRENT_ROUND_DELTA_KEY, context);
@@ -50,14 +50,15 @@ import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.javac.*;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsProject;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.java.LanguageLevel;
import org.jetbrains.jps.model.JpsSimpleElement;
import org.jetbrains.jps.model.java.*;
import org.jetbrains.jps.model.java.compiler.*;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.module.JpsModuleType;
import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot;
import org.jetbrains.jps.service.JpsServiceManager;
import org.jetbrains.jps.util.JpsPathUtil;
import javax.tools.*;
import java.io.*;
@@ -247,6 +248,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
exitCode = ExitCode.OK;
final Set<File> srcPath = new HashSet<File>();
collectSourceRoots(chunk, srcPath, chunk.containsTests()? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE);
final BuildRootIndex index = pd.getBuildRootIndex();
for (ModuleBuildTarget target : chunk.getTargets()) {
for (JavaSourceRootDescriptor rd : index.getTempTargetRoots(target, context)) {
@@ -301,6 +303,14 @@ public class JavaBuilder extends ModuleLevelBuilder {
return exitCode;
}
private static void collectSourceRoots(ModuleChunk chunk, Set<File> srcPath, final JavaSourceRootType rootType) {
for (JpsModule module : chunk.getModules()) {
for (JpsTypedModuleSourceRoot<JpsSimpleElement<JavaSourceRootProperties>> root : module.getSourceRoots(rootType)) {
srcPath.add(JpsPathUtil.urlToFile(root.getUrl()));
}
}
}
private boolean compileJava(
final CompileContext context,
ModuleChunk chunk,
@@ -40,7 +40,7 @@ public class JavacMain {
"-d", "-classpath", "-cp", "-bootclasspath"
));
private static final Set<String> FILTERED_SINGLE_OPTIONS = new HashSet<String>(Arrays.<String>asList(
/*javac options*/ "-verbose", "-proc:only", "-implicit:class", "-implicit:none",
/*javac options*/ "-verbose", "-proc:only", "-implicit:class", "-implicit:none", "-Xprefer:newer", "-Xprefer:source",
/*eclipse options*/"-noExit"
));
@@ -211,6 +211,7 @@ public class JavacMain {
private static Collection<String> prepareOptions(final Collection<String> options, boolean usingJavac) {
final List<String> result = new ArrayList<String>();
if (usingJavac) {
result.add("-Xprefer:source");
result.add("-implicit:class"); // the option supported by javac only
}
else { // is Eclipse
@@ -52,4 +52,8 @@ public class MarkDirtyTest extends IncrementalTestCase {
JpsModuleRootModificationUtil.addDependency(util, lib);
doTestBuild(1).assertSuccessful();
}
public void testRecompileTwinDependencies() {
doTest().assertSuccessful();
}
}