[java] allows one module-info file in either source or test root - compilation (IDEA-171221)

This commit is contained in:
Roman Shevchenko
2017-04-11 15:33:06 +02:00
parent ce8da76d32
commit befc809e2d
4 changed files with 60 additions and 50 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -24,6 +24,7 @@ import com.intellij.openapi.compiler.CompilerManager
import com.intellij.openapi.compiler.CompilerMessageCategory
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.psi.PsiJavaModule
import com.intellij.psi.search.FilenameIndex
import org.jetbrains.jps.builders.impl.BuildDataPathsImpl
@@ -35,37 +36,50 @@ class JavaModuleIndexBuildTask : CompileTask {
val systemDir = BuildManager.getInstance().getProjectSystemDirectory(project)
if (systemDir == null) {
context.addMessage(CompilerMessageCategory.ERROR, "Internal error: no system directory for project: ${project}", null, 0, 0)
context.error("Internal error: no system directory for project: ${project}")
return false
}
val map = runReadAction {
val compilerManager = CompilerManager.getInstance(project)
ModuleManager.getInstance(project).modules.asSequence()
.map {
val files = FilenameIndex.getVirtualFilesByName(project, PsiJavaModule.MODULE_INFO_FILE, it.getModuleScope(false))
it.name to files.filter { !compilerManager.isExcludedFromCompilation(it) }
val badModules = mutableListOf<String>()
val paths = mutableMapOf<String, String?>()
val compilerManager = CompilerManager.getInstance(project)
runReadAction {
ModuleManager.getInstance(project).modules.forEach { module ->
val index = ModuleRootManager.getInstance(module).fileIndex
val sourceFiles = mutableListOf<String>()
val testFiles = mutableListOf<String>()
FilenameIndex.getVirtualFilesByName(project, PsiJavaModule.MODULE_INFO_FILE, module.moduleScope).forEach { file ->
if (!compilerManager.isExcludedFromCompilation(file)) {
(if (index.isInTestSourceContent(file)) testFiles else sourceFiles) += file.path
}
}
.toMap()
if (sourceFiles.size + testFiles.size > 1) {
badModules += module.name
}
else {
paths += module.name + JavaModuleIndexImpl.SOURCE_SUFFIX to sourceFiles.firstOrNull()
paths += module.name + JavaModuleIndexImpl.TEST_SUFFIX to testFiles.firstOrNull()
}
}
}
val errors = map.filter { it.value.size > 1 }.map { IdeBundle.message("compiler.multiple.module.descriptors", it.key) }
if (errors.isNotEmpty()) {
errors.forEach { context.addMessage(CompilerMessageCategory.ERROR, it, null, 0, 0) }
if (badModules.isNotEmpty()) {
badModules.forEach { context.error(IdeBundle.message("compiler.multiple.module.descriptors", it)) }
return false
}
val paths = map.map { it.key to it.value.firstOrNull()?.path }.toMap()
try {
JavaModuleIndexImpl.store(BuildDataPathsImpl(systemDir).dataStorageRoot, paths)
}
catch(e: Exception) {
Logger.getInstance(JavaModuleIndexBuildTask::class.java).error(e)
context.addMessage(CompilerMessageCategory.ERROR, "Internal error: can't save module index: ${e.message}", null, 0, 0)
context.error("Internal error: can't save module index: ${e.message}")
return false
}
return true
}
private fun CompileContext.error(message: String) = addMessage(CompilerMessageCategory.ERROR, message, null, 0, 0)
}
@@ -235,12 +235,17 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
});
boolean hasModules = false;
if ((!filesToCompile.isEmpty() || dirtyFilesHolder.hasRemovedFiles()) &&
JpsJavaSdkType.parseVersion(getLanguageLevel(ContainerUtil.getFirstItem(chunk.getModules()))) >= 9) {
// at the moment, there is no incremental compilation for module-info files, so they should be rebuilt on every change
JavaModuleIndex index = getJavaModuleIndex(context);
for (JpsModule module : chunk.getModules()) {
ContainerUtil.addIfNotNull(filesToCompile, index.getModuleInfoFile(module));
for (ModuleBuildTarget target : chunk.getTargets()) {
File moduleInfoFile = index.getModuleInfoFile(target.getModule(), target.isTests());
if (moduleInfoFile != null) {
filesToCompile.add(moduleInfoFile);
hasModules = true;
}
}
}
@@ -251,7 +256,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
return compile(context, chunk, dirtyFilesHolder, filesToCompile, outputConsumer, compilingTool);
return compile(context, chunk, dirtyFilesHolder, filesToCompile, outputConsumer, compilingTool, hasModules);
}
catch (BuildDataCorruptedException | PersistentEnumeratorBase.CorruptedException | ProjectBuildException e) {
throw e;
@@ -270,7 +275,8 @@ public class JavaBuilder extends ModuleLevelBuilder {
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
Collection<File> files,
OutputConsumer outputConsumer,
JavaCompilingTool compilingTool) throws Exception {
JavaCompilingTool compilingTool,
boolean hasModules) throws Exception {
ExitCode exitCode = ExitCode.NOTHING_DONE;
final boolean hasSourcesToCompile = !files.isEmpty();
@@ -323,7 +329,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
try {
compiledOk = compileJava(context, chunk, files, classpath, platformCp, srcPath, diagnosticSink, outputSink, compilingTool);
compiledOk = compileJava(context, chunk, files, classpath, platformCp, srcPath, diagnosticSink, outputSink, compilingTool, hasModules);
}
finally {
// heuristic: incorrect paths data recovery, so that the next make should not contain non-existing sources in 'recompile' list
@@ -370,7 +376,8 @@ public class JavaBuilder extends ModuleLevelBuilder {
Collection<File> sourcePath,
DiagnosticOutputConsumer diagnosticSink,
OutputFileConsumer outputSink,
JavaCompilingTool compilingTool) throws Exception {
JavaCompilingTool compilingTool,
boolean hasModules) throws Exception {
final TasksCounter counter = new TasksCounter();
COUNTER_KEY.set(context, counter);
@@ -395,7 +402,6 @@ public class JavaBuilder extends ModuleLevelBuilder {
try {
final int targetLanguageLevel = JpsJavaSdkType.parseVersion(getLanguageLevel(chunk.getModules().iterator().next()));
final boolean shouldForkJavac = shouldForkCompilerProcess(context, chunk, targetLanguageLevel);
final boolean hasModules = targetLanguageLevel >= 9 && getJavaModuleIndex(context).hasJavaModules(modules);
// when forking external javac, compilers from SDK 1.6 and higher are supported
Pair<String, Integer> forkSdk = null;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -20,17 +20,11 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.module.JpsModule;
import java.io.File;
import java.util.Set;
public abstract class JavaModuleIndex {
/**
* Returns a path to a module descriptor (module-info.java file) for the given module,
* or {@code null} when there is no descriptor.
*/
public abstract @Nullable File getModuleInfoFile(@NotNull JpsModule module);
/**
* Returns {@code true} when at least one module in the chunk has a module descriptor (module-info.java file).
*/
public abstract boolean hasJavaModules(@NotNull Set<JpsModule> chunk);
public abstract @Nullable File getModuleInfoFile(@NotNull JpsModule module, boolean forTests);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -21,6 +21,7 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JavaModuleIndex;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.java.compiler.JpsCompilerExcludes;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
@@ -30,9 +31,11 @@ import java.io.*;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class JavaModuleIndexImpl extends JavaModuleIndex {
public static final String SOURCE_SUFFIX = ":S";
public static final String TEST_SUFFIX = ":T";
private static final String INDEX_PATH = "jigsaw/module-info.map";
private static final String NULL_PATH = "-";
private static final String MODULE_INFO_FILE = "module-info.java";
@@ -50,34 +53,27 @@ public class JavaModuleIndexImpl extends JavaModuleIndex {
myExcludes = null;
}
@Nullable
@Override
public @Nullable File getModuleInfoFile(@NotNull JpsModule module) {
String key = module.getName();
public File getModuleInfoFile(@NotNull JpsModule module, boolean forTests) {
String key = module.getName() + (forTests ? TEST_SUFFIX : SOURCE_SUFFIX);
if (myExcludes == null || myMapping.containsKey(key)) {
return myMapping.get(key);
}
File file = findModuleInfoFile(module);
File file = findModuleInfoFile(module, forTests ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE);
myMapping.put(key, file);
return file;
}
@Override
public boolean hasJavaModules(@NotNull Set<JpsModule> chunk) {
for (JpsModule module : chunk) {
if (getModuleInfoFile(module) != null) {
return true;
}
}
return false;
}
private File findModuleInfoFile(JpsModule module) {
private File findModuleInfoFile(JpsModule module, JavaSourceRootType rootType) {
for (JpsModuleSourceRoot root : module.getSourceRoots()) {
File file = new File(JpsPathUtil.urlToOsPath(root.getUrl()), MODULE_INFO_FILE);
if (file.isFile() && !myExcludes.isExcluded(file)) {
return file;
if (rootType.equals(root.getRootType())) {
File file = new File(JpsPathUtil.urlToOsPath(root.getUrl()), MODULE_INFO_FILE);
if (file.isFile() && !myExcludes.isExcluded(file)) {
return file;
}
}
}