support cyclic module deps compilation for groovy

This commit is contained in:
peter
2012-05-25 17:35:24 +02:00
parent bbf3ab6a20
commit 74ce818ce5
4 changed files with 118 additions and 32 deletions
@@ -32,23 +32,22 @@ public class IdeaTestUtil extends PlatformTestUtil {
public static void withLevel(final Module module, final LanguageLevel level, final Runnable r) {
final LanguageLevelProjectExtension projectExt = LanguageLevelProjectExtension.getInstance(module.getProject());
final LanguageLevelModuleExtension moduleExt = LanguageLevelModuleExtension.getInstance(module);
final LanguageLevel projectLevel = projectExt.getLanguageLevel();
final LanguageLevel moduleLevel = moduleExt.getLanguageLevel();
final LanguageLevel moduleLevel = LanguageLevelModuleExtension.getInstance(module).getLanguageLevel();
try {
projectExt.setLanguageLevel(level);
setModuleLanguageLevel(level, moduleExt);
setModuleLanguageLevel(module, level);
r.run();
}
finally {
setModuleLanguageLevel(moduleLevel, moduleExt);
setModuleLanguageLevel(module, moduleLevel);
projectExt.setLanguageLevel(projectLevel);
}
}
private static void setModuleLanguageLevel(final LanguageLevel level, final LanguageLevelModuleExtension moduleExt) {
final LanguageLevelModuleExtension modifiable = (LanguageLevelModuleExtension)moduleExt.getModifiableModel(true);
public static void setModuleLanguageLevel(Module module, final LanguageLevel level) {
final LanguageLevelModuleExtension modifiable = (LanguageLevelModuleExtension)LanguageLevelModuleExtension.getInstance(module).getModifiableModel(true);
modifiable.setLanguageLevel(level);
modifiable.commit();
}
@@ -52,23 +52,23 @@ public class GroovyBuilder extends ModuleLevelBuilder {
return ExitCode.NOTHING_DONE;
}
String moduleOutput = getModuleOutput(context, chunk);
String compilerOutput = getCompilerOutput(moduleOutput);
Map<Module, String> finalOutputs = getCanonicalModuleOutputs(context, chunk);
Map<Module, String> generationOutputs = getGenerationOutputs(chunk, finalOutputs);
final Set<String> toCompilePaths = new LinkedHashSet<String>();
for (File file : toCompile) {
toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
}
Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutput);
Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, finalOutputs);
final String encoding = context.getProjectDescriptor().getEncodingConfiguration().getPreferredModuleChunkEncoding(chunk);
List<String> patchers = Collections.emptyList(); //todo patchers
String compilerOutput = generationOutputs.get(chunk.representativeModule());
final File tempFile = GroovycOSProcessHandler.fillFileWithGroovycParameters(
compilerOutput, toCompilePaths, FileUtil.toSystemDependentName(moduleOutput), class2Src, encoding, patchers
compilerOutput, toCompilePaths, finalOutputs.get(chunk.representativeModule()), class2Src, encoding, patchers
);
//todo different outputs in a chunk
//todo xmx
final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
getJavaExecutable(chunk),
@@ -98,13 +98,24 @@ public class GroovyBuilder extends ModuleLevelBuilder {
}
if (myForStubs) {
JavaBuilder.addTempSourcePathRoot(context, new File(compilerOutput));
for (Module module : generationOutputs.keySet()) {
File root = new File(generationOutputs.get(module));
context.getRootsIndex().associateRoot(root, module, context.isCompilingTests(), true);
JavaBuilder.addTempSourcePathRoot(context, root);
}
}
for (CompilerMessage message : handler.getCompilerMessages()) {
context.processMessage(message);
}
if (!myForStubs && updateDependencies(context, chunk, toCompile, moduleOutput, handler.getSuccessfullyCompiled())) {
List<GroovycOSProcessHandler.OutputItem> compiled = new ArrayList<GroovycOSProcessHandler.OutputItem>();
for (GroovycOSProcessHandler.OutputItem item : handler.getSuccessfullyCompiled()) {
compiled.add(ensureCorrectOutput(context, chunk, item, generationOutputs, compilerOutput));
}
if (!myForStubs && updateDependencies(context, chunk, toCompile, generationOutputs, compiled)) {
return ExitCode.ADDITIONAL_PASS_REQUIRED;
}
return ExitCode.OK;
@@ -114,6 +125,48 @@ public class GroovyBuilder extends ModuleLevelBuilder {
}
}
private Map<Module, String> getGenerationOutputs(ModuleChunk chunk, Map<Module, String> finalOutputs) throws IOException {
Map<Module, String> generationOutputs = new HashMap<Module, String>();
for (Module module : chunk.getModules()) {
generationOutputs.put(module, myForStubs ? FileUtil.createTempDirectory("groovyStubs", module.getName()).getPath() : finalOutputs.get(module));
}
return generationOutputs;
}
private static Map<Module, String> getCanonicalModuleOutputs(CompileContext context, ModuleChunk chunk) {
Map<Module, String> finalOutputs = new HashMap<Module, String>();
for (Module module : chunk.getModules()) {
File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(module, context.isCompilingTests());
assert moduleOutputDir != null;
String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
assert moduleOutputPath != null;
finalOutputs.put(module, moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/");
}
return finalOutputs;
}
private static GroovycOSProcessHandler.OutputItem ensureCorrectOutput(CompileContext context,
ModuleChunk chunk,
GroovycOSProcessHandler.OutputItem item, Map<Module, String> generationOutputs, String compilerOutput) throws IOException {
if (chunk.getModules().size() > 1) {
RootDescriptor descriptor = context.getModuleAndRoot(new File(item.sourcePath));
if (descriptor != null) {
Module srcModule = context.getProject().getModules().get(descriptor.module);
if (srcModule != null && srcModule != chunk.representativeModule()) {
File output = new File(item.outputPath);
//todo honor package prefixes
File correctRoot = new File(generationOutputs.get(srcModule));
File correctOutput = new File(correctRoot, FileUtil.getRelativePath(new File(compilerOutput), output));
FileUtil.rename(output, correctOutput);
return new GroovycOSProcessHandler.OutputItem(correctOutput.getPath(), item.sourcePath);
}
}
}
return item;
}
private static String getJavaExecutable(ModuleChunk chunk) {
Sdk sdk = chunk.getModules().iterator().next().getSdk();
if (sdk instanceof JavaSdk) {
@@ -122,23 +175,11 @@ public class GroovyBuilder extends ModuleLevelBuilder {
return SystemProperties.getJavaHome() + "/bin/java";
}
private static String getModuleOutput(CompileContext context, ModuleChunk chunk) {
final Module representativeModule = chunk.getModules().iterator().next();
File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(representativeModule, context.isCompilingTests());
assert moduleOutputDir != null;
String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
return moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/";
}
@Override
public boolean shouldHonorFileEncodingForCompilation(File file) {
return isGroovyFile(file.getAbsolutePath());
}
private String getCompilerOutput(String moduleOutputDir) throws IOException {
return FileUtil.toCanonicalPath((myForStubs ? FileUtil.createTempDirectory("groovyStubs", null) : new File(moduleOutputDir)).getPath());
}
private static List<File> collectChangedFiles(CompileContext context, ModuleChunk chunk) throws IOException {
final List<File> toCompile = new ArrayList<File>();
context.processFilesToRecompile(chunk, new FileProcessor() {
@@ -156,7 +197,7 @@ public class GroovyBuilder extends ModuleLevelBuilder {
private boolean updateDependencies(CompileContext context,
ModuleChunk chunk,
List<File> toCompile,
String moduleOutputPath,
Map<Module, String> generationOutputs,
List<GroovycOSProcessHandler.OutputItem> successfullyCompiled) throws IOException {
final Mappings delta = context.createDelta();
final List<File> successfullyCompiledFiles = new ArrayList<File>();
@@ -172,11 +213,11 @@ public class GroovyBuilder extends ModuleLevelBuilder {
if (moduleAndRoot != null) {
final String moduleName = moduleAndRoot.module;
context.getDataManager().getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot).appendData(sourcePath, outputPath);
String moduleOutputPath = generationOutputs.get(context.getProject().getModules().get(moduleName));
generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/'));
}
callback.associate(outputPath, sourcePath, new ClassReader(FileUtil.loadFileBytes(new File(outputPath))));
successfullyCompiledFiles.add(new File(sourcePath));
generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/'));
}
context.processMessage(generatedEvent);
@@ -205,9 +246,10 @@ public class GroovyBuilder extends ModuleLevelBuilder {
return path.endsWith(".groovy") || path.endsWith(".gpp");
}
private static Map<String, String> buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, String moduleOutputPath) throws IOException {
private static Map<String, String> buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, Map<Module, String> finalOutputs) throws IOException {
final Map<String, String> class2Src = new HashMap<String, String>();
for (Module module : chunk.getModules()) {
String moduleOutputPath = finalOutputs.get(module);
final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(module.getName(), context.isCompilingTests());
for (String src : srcToOut.getKeys()) {
if (!toCompilePaths.contains(src) && isGroovyFile(src) &&
@@ -567,6 +567,42 @@ class Main {
assertEmpty make()
}
public void "test module cycle"() {
def dep = addDependentModule()
PsiTestUtil.addDependency(myModule, dep)
addGroovyLibrary(dep)
myFixture.addFileToProject('Foo.groovy', 'class Foo extends Bar { static void main(String[] args) { println "Hello from Foo" } }')
myFixture.addFileToProject('FooX.java', 'class FooX extends Bar { }')
myFixture.addFileToProject("dependent/Bar.groovy", "class Bar { Foo f; static void main(String[] args) { println 'Hello from Bar' } }")
myFixture.addFileToProject("dependent/BarX.java", "class BarX { Foo f; }")
def checkClassFiles = {
assert findClassFile('Foo', myModule)
assert findClassFile('FooX', myModule)
assert findClassFile('Bar', dep)
assert findClassFile('BarX', dep)
assert !findClassFile('Bar', myModule)
assert !findClassFile('BarX', myModule)
assert !findClassFile('Foo', dep)
assert !findClassFile('FooX', dep)
}
println '1'
assertEmpty make()
checkClassFiles()
println '2'
assertEmpty make()
checkClassFiles()
assertOutput('Foo', 'Hello from Foo', myModule)
assertOutput('Bar', 'Hello from Bar', dep)
checkClassFiles()
}
public void testCompileTimeConstants() {
myFixture.addFileToProject 'Gr.groovy', '''
interface Gr {
@@ -51,6 +51,7 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
@@ -184,8 +185,10 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC
final ContentEntry entry = model.addContentEntry(depRoot);
entry.addSourceFolder(depRoot, false);
model.setSdk(ModuleRootManager.getInstance(myModule).getSdk());
model.commit();
IdeaTestUtil.setModuleLanguageLevel(dep, LanguageLevelModuleExtension.getInstance(myModule).getLanguageLevel());
result.setResult(dep);
}
}.execute().getResultObject();
@@ -208,9 +211,15 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC
}
@Nullable protected VirtualFile findClassFile(String className) {
final CompilerModuleExtension extension = ModuleRootManager.getInstance(myModule).getModuleExtension(CompilerModuleExtension.class);
return findClassFile(className, myModule);
}
@Nullable protected VirtualFile findClassFile(String className, Module module) {
//noinspection ConstantConditions
return extension.getCompilerOutputPath().findChild(className + ".class");
VirtualFile path = ModuleRootManager.getInstance(module).getModuleExtension(CompilerModuleExtension.class).getCompilerOutputPath();
path.getChildren();
assert path != null;
path.refresh(false, true);
return path.findChild(className + ".class");
}
protected static void touch(VirtualFile file) throws IOException {