diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java index dbe6eecef318..8070a4a9f556 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java @@ -1,5 +1,6 @@ package org.jetbrains.jps.incremental; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.jps.idea.OwnServiceLoader; import java.util.*; @@ -65,6 +66,10 @@ public class BuilderRegistry { return Collections.unmodifiableList(myModuleLevelBuilders.get(category)); // todo } + public List getAllModuleLevelBuilders() { + return ContainerUtil.concat(myModuleLevelBuilders.values()); + } + public List getProjectLevelBuilders() { return myProjectLevelBuilders; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java new file mode 100644 index 000000000000..62bedef2151b --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompilerEncodingConfiguration.java @@ -0,0 +1,107 @@ +package org.jetbrains.jps.incremental; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.Module; +import org.jetbrains.jps.ModuleChunk; +import org.jetbrains.jps.incremental.fs.RootDescriptor; + +import java.io.File; +import java.util.*; + +/** + * @author nik + */ +public class CompilerEncodingConfiguration { + private final Map myFilePathToCharset; + private final ModuleRootsIndex myRootsIndex; + private Map> myModuleCharsetMap; + + public CompilerEncodingConfiguration(Map filePathToCharset, ModuleRootsIndex rootsIndex) { + myFilePathToCharset = filePathToCharset; + myRootsIndex = rootsIndex; + } + + public Map> getModuleCharsetMap() { + if (myModuleCharsetMap == null) { + myModuleCharsetMap = computeModuleCharsetMap(); + } + return myModuleCharsetMap; + } + + private Map> computeModuleCharsetMap() { + final Map> map = new THashMap>(); + final List builders = BuilderRegistry.getInstance().getAllModuleLevelBuilders(); + for (Map.Entry entry : myFilePathToCharset.entrySet()) { + final String filePath = entry.getKey(); + final String charset = entry.getValue(); + File file = new File(FileUtil.toSystemDependentName(filePath)); + if (charset == null || (!file.isDirectory() && !shouldHonorEncodingForCompilation(builders, file))) continue; + + final RootDescriptor rootDescriptor = myRootsIndex.getModuleAndRoot(file); + if (rootDescriptor == null) continue; + + final String module = rootDescriptor.module; + Set set = map.get(module); + if (set == null) { + set = new LinkedHashSet(); + map.put(module, set); + + final File sourceRoot = rootDescriptor.root; + File current = FileUtil.getParentFile(file); + String parentCharset = null; + while (current != null) { + final String currentCharset = myFilePathToCharset.get(FileUtil.toSystemIndependentName(current.getAbsolutePath())); + if (currentCharset != null) { + parentCharset = currentCharset; + } + if (FileUtil.filesEqual(current, sourceRoot)) { + break; + } + current = FileUtil.getParentFile(current); + } + if (parentCharset != null) { + set.add(parentCharset); + } + } + set.add(charset); + } + + return map; + } + + private static boolean shouldHonorEncodingForCompilation(List builders, File file) { + for (ModuleLevelBuilder builder : builders) { + if (builder.shouldHonorFileEncodingForCompilation(file)) { + return true; + } + } + return false; + } + + @Nullable + public String getPreferredModuleChunkEncoding(@NotNull ModuleChunk moduleChunk) { + for (Module module : moduleChunk.getModules()) { + final Set encodings = getModuleCharsetMap().get(module.getName()); + final String encoding = ContainerUtil.getFirstItem(encodings, null); + if (encoding != null) return encoding; + } + return null; + } + + @NotNull + public Set getAllModuleChunkEncodings(@NotNull ModuleChunk moduleChunk) { + final Map> map = getModuleCharsetMap(); + Set encodings = new HashSet(); + for (Module module : moduleChunk.getModules()) { + final Set moduleEncodings = map.get(module.getName()); + if (moduleEncodings != null) { + encodings.addAll(moduleEncodings); + } + } + return encodings; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java index 014c9ff487f3..f480fde5b38f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java @@ -41,6 +41,10 @@ public abstract class ModuleLevelBuilder extends Builder { public abstract ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException; + public boolean shouldHonorFileEncodingForCompilation(File file) { + return false; + } + public final BuilderCategory getCategory() { return myCategory; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 38f7516f288c..d7a5483a6608 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -223,6 +223,11 @@ public class JavaBuilder extends ModuleLevelBuilder { } } + @Override + public boolean shouldHonorFileEncodingForCompilation(File file) { + return JAVA_SOURCES_FILTER.accept(file) || FORM_SOURCES_FILTER.accept(file); + } + @Nullable private static File getBoundSource(File srcRoot, File formFile) throws IOException { final String boundClassName = FormsParsing.readBoundClassName(formFile); diff --git a/jps/jps-builders/src/org/jetbrains/jps/server/ProjectDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/server/ProjectDescriptor.java index e62b7c878921..c0fa5acbc087 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/server/ProjectDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/server/ProjectDescriptor.java @@ -5,6 +5,7 @@ import org.jetbrains.jps.Module; import org.jetbrains.jps.Project; import org.jetbrains.jps.Sdk; import org.jetbrains.jps.incremental.BuildLoggingManager; +import org.jetbrains.jps.incremental.CompilerEncodingConfiguration; import org.jetbrains.jps.incremental.ModuleRootsIndex; import org.jetbrains.jps.incremental.fs.BuildFSState; import org.jetbrains.jps.incremental.storage.BuildDataManager; @@ -27,6 +28,7 @@ public final class ProjectDescriptor { public ModuleRootsIndex rootsIndex; private int myUseCounter = 1; private Set myProjectJavaSdks; + private CompilerEncodingConfiguration myEncodingConfiguration; public ProjectDescriptor(Project project, BuildFSState fsState, @@ -40,6 +42,7 @@ public final class ProjectDescriptor { myLoggingManager = loggingManager; this.rootsIndex = new ModuleRootsIndex(project); myProjectJavaSdks = new HashSet(); + myEncodingConfiguration = new CompilerEncodingConfiguration(project.getFilePathToCharset(), rootsIndex); for (Module module : project.getModules().values()) { final Sdk sdk = module.getSdk(); if (sdk instanceof JavaSdk && !myProjectJavaSdks.contains(sdk)) { @@ -51,6 +54,10 @@ public final class ProjectDescriptor { } } + public CompilerEncodingConfiguration getEncodingConfiguration() { + return myEncodingConfiguration; + } + public Set getProjectJavaSdks() { return myProjectJavaSdks; } diff --git a/jps/model/src/org/jetbrains/jps/Project.groovy b/jps/model/src/org/jetbrains/jps/Project.groovy index d08da5c5ed64..bc330008cf39 100644 --- a/jps/model/src/org/jetbrains/jps/Project.groovy +++ b/jps/model/src/org/jetbrains/jps/Project.groovy @@ -22,6 +22,7 @@ class Project { final IgnoredFilePatterns ignoredFilePatterns = new IgnoredFilePatterns() String projectCharset; // contains project charset, if not specified default charset will be used (used by compilers) + Map filePathToCharset = [:]; def Project() { } diff --git a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index a500b889211d..d40e9cc91e9b 100644 --- a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -340,6 +340,10 @@ public class IdeaProjectLoader { if ("PROJECT".equals(url)) { project.projectCharset = charset; } + else { + def path = projectMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(url)); + project.filePathToCharset[path] = charset; + } } } diff --git a/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index d854a99bf2c6..f23b54077dd6 100644 --- a/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/plugins/groovy/rt/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java @@ -132,6 +132,11 @@ public class GroovyBuilder extends ModuleLevelBuilder { 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()); }