support JPMS module names for exploded automatic modules (IDEA-183692)

GitOrigin-RevId: 29b82f6f8ebce48a71cd1ca74ec70532697e93a7
This commit is contained in:
Eugene Zhuravlev
2019-10-18 13:09:55 +00:00
committed by intellij-monorepo-bot
parent 075c0d463a
commit a12101438d
21 changed files with 741 additions and 187 deletions
@@ -45,7 +45,8 @@ import org.jetbrains.jps.incremental.BinaryContent;
import org.jetbrains.jps.javac.*;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import javax.tools.*;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.net.URI;
@@ -415,7 +416,7 @@ public class CompilerManagerImpl extends CompilerManager {
final Map<File, Set<File>> outs = Collections.singletonMap(outputDir, sourceRoots);
final ExternalJavacManager javacManager = getJavacManager();
final CompilationPaths paths = CompilationPaths.create(platformCp, classpath, upgradeModulePath, modulePath, sourcePath);
final CompilationPaths paths = CompilationPaths.create(platformCp, classpath, upgradeModulePath, ModulePath.create(modulePath), sourcePath);
// do not keep process alive in tests since every test expects all spawned processes to terminate in teardown
boolean compiledOk = javacManager != null && javacManager.forkJavac(
javaHome, -1, Collections.emptyList(), options, paths, files, outs, diagnostic, outputCollector,
@@ -8,6 +8,7 @@ import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.io.Compressor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.javac.JpsJavacFileManager;
import org.jetbrains.jps.javac.OutputFileObject;
import org.jetbrains.jps.javac.ZipFileObject;
@@ -66,27 +67,7 @@ public class JavaCompilerBasicTest extends BaseCompilerTestCase {
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
}
}, Locale.US, null);
final JpsJavacFileManager fileManager = new JpsJavacFileManager(new JpsJavacFileManager.Context() {
@Override
public boolean isCanceled() {
return false;
}
@NotNull
@Override
public StandardJavaFileManager getStandardFileManager() {
return stdFileManager;
}
@Override
public void consumeOutputFile(@NotNull OutputFileObject obj) {
}
@Override
public void reportMessage(Diagnostic.Kind kind, String message) {
}
}, true, Collections.emptyList());
final JpsJavacFileManager fileManager = new JpsJavacFileManager(new DummyContext(stdFileManager), true, Collections.emptyList());
fileManager.setLocation(StandardLocation.CLASS_PATH, Collections.singleton(jarFile));
fileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.emptyList());
@@ -123,27 +104,7 @@ public class JavaCompilerBasicTest extends BaseCompilerTestCase {
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
}
}, Locale.US, null);
final JpsJavacFileManager fileManager = new JpsJavacFileManager(new JpsJavacFileManager.Context() {
@Override
public boolean isCanceled() {
return false;
}
@NotNull
@Override
public StandardJavaFileManager getStandardFileManager() {
return stdFileManager;
}
@Override
public void consumeOutputFile(@NotNull OutputFileObject obj) {
}
@Override
public void reportMessage(Diagnostic.Kind kind, String message) {
}
}, true, Collections.emptyList());
final JpsJavacFileManager fileManager = new JpsJavacFileManager(new DummyContext(stdFileManager), true, Collections.emptyList());
fileManager.setLocation(StandardLocation.CLASS_PATH, Collections.singleton(jarFile));
fileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.emptyList());
@@ -187,7 +148,7 @@ public class JavaCompilerBasicTest extends BaseCompilerTestCase {
private static void checkFileObjectsBelongToLocation(JpsJavacFileManager fileManager,
final JavaFileManager.Location location,
Iterable<? extends FileObject> fileObjects) {
Iterable<? extends FileObject> fileObjects) throws IOException {
for (FileObject source : fileObjects) {
assertTrue(source.getName() + " should belong to " + location.getName(), fileManager.contains(location, source));
}
@@ -212,4 +173,37 @@ public class JavaCompilerBasicTest extends BaseCompilerTestCase {
make(module);
assertOutput(module, fs().file("A.class"));
}
private static final class DummyContext implements JpsJavacFileManager.Context {
private final StandardJavaFileManager myStdFileManager;
DummyContext(StandardJavaFileManager stdFileManager) {
myStdFileManager = stdFileManager;
}
@Nullable
@Override
public String getExplodedAutomaticModuleName(File pathElement) {
return null;
}
@Override
public boolean isCanceled() {
return false;
}
@NotNull
@Override
public StandardJavaFileManager getStandardFileManager() {
return myStdFileManager;
}
@Override
public void consumeOutputFile(@NotNull OutputFileObject obj) {
}
@Override
public void reportMessage(Diagnostic.Kind kind, String message) {
}
}
}
@@ -0,0 +1,7 @@
package ppp;
public class Util {
public static void perform() {
System.out.println("Util.perform");
}
}
@@ -0,0 +1,9 @@
package qqq;
import ppp.Util;
public class Main {
public static void main(String[] args) {
Util.perform();
}
}
@@ -0,0 +1,7 @@
package ppp;
public class Util {
public static void perform() {
System.out.println("Util.perform");
}
}
@@ -0,0 +1,9 @@
package qqq;
import ppp.Util;
public class Main {
public static void main(String[] args) {
Util.perform();
}
}
@@ -44,6 +44,7 @@ message Message {
repeated OutputGroup output = 7;
repeated string module_path = 8;
repeated string upgrade_module_path = 9;
map<string,string> module_names = 10;
}
message Response {
@@ -142,7 +142,7 @@ public class ExternalJavacProcess {
Collection<? extends File> files,
Collection<? extends File> classpath,
Collection<? extends File> platformCp,
Collection<? extends File> modulePath,
ModulePath modulePath,
Collection<? extends File> upgradeModulePath,
Collection<? extends File> sourcePath,
Map<File, Set<File>> outs,
@@ -237,7 +237,14 @@ public class ExternalJavacProcess {
final List<File> cp = toFiles(request.getClasspathList());
final List<File> platformCp = toFiles(request.getPlatformClasspathList());
final List<File> srcPath = toFiles(request.getSourcepathList());
final List<File> modulePath = toFiles(request.getModulePathList());
final ModulePath.Builder modulePathBuilder = ModulePath.newBuilder();
final Map<String, String> namesMap = request.getModuleNamesMap();
for (String path : request.getModulePathList()) {
modulePathBuilder.add(namesMap.get(path), new File(path));
}
final ModulePath modulePath = modulePathBuilder.create();
final List<File> upgradeModulePath = toFiles(request.getUpgradeModulePathList());
final Map<File, Set<File>> outs = new HashMap<File, Set<File>>();
@@ -40,7 +40,7 @@ public class JavacMain {
final Collection<? extends File> sources,
Collection<? extends File> classpath,
Collection<? extends File> platformClasspath,
Collection<? extends File> modulePath,
ModulePath modulePath,
Collection<? extends File> upgradeModulePath,
Collection<? extends File> sourcePath,
final Map<File, Set<File>> outputDirToRoots,
@@ -63,7 +63,7 @@ public class JavacMain {
final boolean usingJavac = compilingTool instanceof JavacCompilerTool;
final boolean javacBefore9 = isJavacBefore9(compilingTool);
final JpsJavacFileManager fileManager = new JpsJavacFileManager(
new ContextImpl(compiler, diagnosticConsumer, outputSink, canceledStatus), javacBefore9, JavaSourceTransformer.getTransformers()
new ContextImpl(compiler, diagnosticConsumer, outputSink, modulePath, canceledStatus), javacBefore9, JavaSourceTransformer.getTransformers()
);
if (!platformClasspath.isEmpty()) {
// for javac6 this will prevent lazy initialization of Paths.bootClassPathRtJar
@@ -120,12 +120,12 @@ public class JavacMain {
if (!modulePath.isEmpty()) {
try {
setLocation(fileManager, "MODULE_PATH", modulePath);
setLocation(fileManager, "MODULE_PATH", modulePath.getPath());
if (isAnnotationProcessingEnabled(_options) &&
getLocation(fileManager, "ANNOTATION_PROCESSOR_MODULE_PATH") == null &&
fileManager.getLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH) == null) {
// default annotation processing discovery path to module path if not explicitly set
setLocation(fileManager, "ANNOTATION_PROCESSOR_MODULE_PATH", JpsJavacFileManager.filter(modulePath, new BooleanFunction<File>() {
setLocation(fileManager, "ANNOTATION_PROCESSOR_MODULE_PATH", JpsJavacFileManager.filter(modulePath.getPath(), new BooleanFunction<File>() {
@Override
public boolean fun(File file) {
return !outputDirToRoots.containsKey(file);
@@ -534,15 +534,27 @@ public class JavacMain {
private final StandardJavaFileManager myStdManager;
private final DiagnosticOutputConsumer myOutConsumer;
private final OutputFileConsumer myOutputFileSink;
private final ModulePath myModulePath;
private final CanceledStatus myCanceledStatus;
ContextImpl(@NotNull JavaCompiler compiler, @NotNull DiagnosticOutputConsumer outConsumer, @NotNull OutputFileConsumer sink, CanceledStatus canceledStatus) {
ContextImpl(@NotNull JavaCompiler compiler,
@NotNull DiagnosticOutputConsumer outConsumer,
@NotNull OutputFileConsumer sink,
@NotNull ModulePath modulePath,
CanceledStatus canceledStatus) {
myOutConsumer = outConsumer;
myOutputFileSink = sink;
myModulePath = modulePath;
myCanceledStatus = canceledStatus;
myStdManager = compiler.getStandardFileManager(outConsumer, Locale.US, null);
}
@Nullable
@Override
public String getExplodedAutomaticModuleName(File pathElement) {
return myModulePath.getModuleName(pathElement);
}
@Override
public boolean isCanceled() {
return myCanceledStatus.isCanceled();
@@ -6,7 +6,9 @@ import com.intellij.openapi.util.io.FileUtilRt;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.BinaryContent;
import javax.tools.*;
import javax.tools.Diagnostic;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
@@ -30,7 +32,7 @@ public class JavacProtoUtil {
Collection<? extends File> files,
Collection<? extends File> classpath,
Collection<? extends File> platformCp,
Collection<? extends File> modulePath,
ModulePath modulePath,
Collection<? extends File> upgradeModulePath,
Collection<? extends File> sourcePath,
Map<File, Set<File>> outs) {
@@ -46,8 +48,13 @@ public class JavacProtoUtil {
for (File file : platformCp) {
builder.addPlatformClasspath(FileUtilRt.toSystemIndependentName(file.getPath()));
}
for (File file : modulePath) {
builder.addModulePath(FileUtilRt.toSystemIndependentName(file.getPath()));
for (File file : modulePath.getPath()) {
final String pathEntry = FileUtilRt.toSystemIndependentName(file.getPath());
builder.addModulePath(pathEntry);
final String moduleName = modulePath.getModuleName(file);
if (moduleName != null) {
builder.putModuleNames(pathEntry, moduleName);
}
}
for (File file : upgradeModulePath) {
builder.addUpgradeModulePath(FileUtilRt.toSystemIndependentName(file.getPath()));
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: javac_remote_proto.proto
@@ -1277,6 +1277,40 @@ public final class JavacRemoteProto {
*/
com.google.protobuf.ByteString
getUpgradeModulePathBytes(int index);
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
int getModuleNamesCount();
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
boolean containsModuleNames(
java.lang.String key);
/**
* Use {@link #getModuleNamesMap()} instead.
*/
@java.lang.Deprecated
java.util.Map<java.lang.String, java.lang.String>
getModuleNames();
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
java.util.Map<java.lang.String, java.lang.String>
getModuleNamesMap();
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
java.lang.String getModuleNamesOrDefault(
java.lang.String key,
java.lang.String defaultValue);
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
java.lang.String getModuleNamesOrThrow(
java.lang.String key);
}
/**
* Protobuf type {@code org.jetbrains.javac.Message.Request}
@@ -2672,6 +2706,93 @@ public final class JavacRemoteProto {
upgradeModulePath_.add(value.toStringUtf8());
}
public static final int MODULE_NAMES_FIELD_NUMBER = 10;
private static final class ModuleNamesDefaultEntryHolder {
static final com.google.protobuf.MapEntryLite<
java.lang.String, java.lang.String> defaultEntry =
com.google.protobuf.MapEntryLite
.<java.lang.String, java.lang.String>newDefaultInstance(
com.google.protobuf.WireFormat.FieldType.STRING,
"",
com.google.protobuf.WireFormat.FieldType.STRING,
"");
}
private com.google.protobuf.MapFieldLite<
java.lang.String, java.lang.String> moduleNames_ =
com.google.protobuf.MapFieldLite.emptyMapField();
private com.google.protobuf.MapFieldLite<java.lang.String, java.lang.String>
internalGetModuleNames() {
return moduleNames_;
}
private com.google.protobuf.MapFieldLite<java.lang.String, java.lang.String>
internalGetMutableModuleNames() {
if (!moduleNames_.isMutable()) {
moduleNames_ = moduleNames_.mutableCopy();
}
return moduleNames_;
}
public int getModuleNamesCount() {
return internalGetModuleNames().size();
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public boolean containsModuleNames(
java.lang.String key) {
if (key == null) { throw new java.lang.NullPointerException(); }
return internalGetModuleNames().containsKey(key);
}
/**
* Use {@link #getModuleNamesMap()} instead.
*/
@java.lang.Deprecated
public java.util.Map<java.lang.String, java.lang.String> getModuleNames() {
return getModuleNamesMap();
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public java.util.Map<java.lang.String, java.lang.String> getModuleNamesMap() {
return java.util.Collections.unmodifiableMap(
internalGetModuleNames());
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public java.lang.String getModuleNamesOrDefault(
java.lang.String key,
java.lang.String defaultValue) {
if (key == null) { throw new java.lang.NullPointerException(); }
java.util.Map<java.lang.String, java.lang.String> map =
internalGetModuleNames();
return map.containsKey(key) ? map.get(key) : defaultValue;
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public java.lang.String getModuleNamesOrThrow(
java.lang.String key) {
if (key == null) { throw new java.lang.NullPointerException(); }
java.util.Map<java.lang.String, java.lang.String> map =
internalGetModuleNames();
if (!map.containsKey(key)) {
throw new java.lang.IllegalArgumentException();
}
return map.get(key);
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
private java.util.Map<java.lang.String, java.lang.String>
getMutableModuleNamesMap() {
return internalGetMutableModuleNames();
}
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (((bitField0_ & 0x00000001) == 0x00000001)) {
@@ -2701,6 +2822,11 @@ public final class JavacRemoteProto {
for (int i = 0; i < upgradeModulePath_.size(); i++) {
output.writeString(9, upgradeModulePath_.get(i));
}
for (java.util.Map.Entry<java.lang.String, java.lang.String> entry
: internalGetModuleNames().entrySet()) {
ModuleNamesDefaultEntryHolder.defaultEntry.serializeTo(
output, 10, entry.getKey(), entry.getValue());
}
unknownFields.writeTo(output);
}
@@ -2780,6 +2906,11 @@ public final class JavacRemoteProto {
size += dataSize;
size += 1 * getUpgradeModulePathList().size();
}
for (java.util.Map.Entry<java.lang.String, java.lang.String> entry
: internalGetModuleNames().entrySet()) {
size += ModuleNamesDefaultEntryHolder.defaultEntry.computeMessageSize(
10, entry.getKey(), entry.getValue());
}
size += unknownFields.getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -3510,6 +3641,98 @@ public final class JavacRemoteProto {
return this;
}
public int getModuleNamesCount() {
return instance.getModuleNamesMap().size();
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public boolean containsModuleNames(
java.lang.String key) {
if (key == null) { throw new java.lang.NullPointerException(); }
return instance.getModuleNamesMap().containsKey(key);
}
public Builder clearModuleNames() {
copyOnWrite();
instance.getMutableModuleNamesMap().clear();
return this;
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public Builder removeModuleNames(
java.lang.String key) {
if (key == null) { throw new java.lang.NullPointerException(); }
copyOnWrite();
instance.getMutableModuleNamesMap().remove(key);
return this;
}
/**
* Use {@link #getModuleNamesMap()} instead.
*/
@java.lang.Deprecated
public java.util.Map<java.lang.String, java.lang.String> getModuleNames() {
return getModuleNamesMap();
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public java.util.Map<java.lang.String, java.lang.String> getModuleNamesMap() {
return java.util.Collections.unmodifiableMap(
instance.getModuleNamesMap());
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public java.lang.String getModuleNamesOrDefault(
java.lang.String key,
java.lang.String defaultValue) {
if (key == null) { throw new java.lang.NullPointerException(); }
java.util.Map<java.lang.String, java.lang.String> map =
instance.getModuleNamesMap();
return map.containsKey(key) ? map.get(key) : defaultValue;
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public java.lang.String getModuleNamesOrThrow(
java.lang.String key) {
if (key == null) { throw new java.lang.NullPointerException(); }
java.util.Map<java.lang.String, java.lang.String> map =
instance.getModuleNamesMap();
if (!map.containsKey(key)) {
throw new java.lang.IllegalArgumentException();
}
return map.get(key);
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public Builder putModuleNames(
java.lang.String key,
java.lang.String value) {
if (key == null) { throw new java.lang.NullPointerException(); }
if (value == null) { throw new java.lang.NullPointerException(); }
copyOnWrite();
instance.getMutableModuleNamesMap().put(key, value);
return this;
}
/**
* <code>map&lt;string, string&gt; module_names = 10;</code>
*/
public Builder putAllModuleNames(
java.util.Map<java.lang.String, java.lang.String> values) {
copyOnWrite();
instance.getMutableModuleNamesMap().putAll(values);
return this;
}
// @@protoc_insertion_point(builder_scope:org.jetbrains.javac.Message.Request)
}
private byte memoizedIsInitialized = 2;
@@ -3547,6 +3770,7 @@ public final class JavacRemoteProto {
output_.makeImmutable();
modulePath_.makeImmutable();
upgradeModulePath_.makeImmutable();
moduleNames_.makeImmutable();
return null;
}
case NEW_BUILDER: {
@@ -3565,6 +3789,8 @@ public final class JavacRemoteProto {
output_= visitor.visitList(output_, other.output_);
modulePath_= visitor.visitList(modulePath_, other.modulePath_);
upgradeModulePath_= visitor.visitList(upgradeModulePath_, other.upgradeModulePath_);
moduleNames_ = visitor.visitMap(
moduleNames_, other.internalGetModuleNames());
if (visitor == com.google.protobuf.GeneratedMessageLite.MergeFromVisitor
.INSTANCE) {
bitField0_ |= other.bitField0_;
@@ -3676,6 +3902,12 @@ public final class JavacRemoteProto {
upgradeModulePath_.add(s);
break;
}
case 82: {
if (!moduleNames_.isMutable()) {
moduleNames_ = moduleNames_.mutableCopy();
}
ModuleNamesDefaultEntryHolder.defaultEntry.parseInto(moduleNames_, input, extensionRegistry); break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -13,6 +13,7 @@ import javax.tools.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
@@ -61,17 +62,7 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
return new File(s);
}
};
private static final Method ourContainsMethod;
static {
Method method = null;
try {
method = JavaFileManager.class.getDeclaredMethod("contains", Location.class, FileObject.class);
}
catch (Throwable ignored) {
}
ourContainsMethod = method;
}
private Map<File, Set<File>> myOutputsMap = Collections.emptyMap();
@Nullable
private String myEncodingName;
@@ -82,6 +73,12 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
myJavacBefore9 = javacBefore9;
mySourceTransformers = transformers;
myContext = new Context() {
@Nullable
@Override
public String getExplodedAutomaticModuleName(File pathElement) {
return context.getExplodedAutomaticModuleName(pathElement);
}
@Override
public boolean isCanceled() {
return context.isCanceled();
@@ -267,6 +264,9 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
}
public interface Context {
@Nullable
String getExplodedAutomaticModuleName(File pathElement);
boolean isCanceled();
@NotNull
@@ -333,6 +333,9 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
@Override
public void setLocation(Location location, Iterable<? extends File> path) throws IOException{
getStdManager().setLocation(location, path);
if ("MODULE_PATH".equals(location.getName())) {
initExplodedModuleNames(location, path);
}
}
@Override
@@ -485,21 +488,15 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
}
// this method overrides corresponding API method since javac 9
public boolean contains(Location location, FileObject fo) {
public boolean contains(Location location, FileObject fo) throws IOException {
if (fo instanceof JpsFileObject) {
return location.equals(((JpsFileObject)fo).getLocation());
}
if (ourContainsMethod == null) {
throw new UnsupportedOperationException("Operation is not supported for file objects " + fo.getClass().getName());
}
// delegate the call further
try {
return ((Boolean)ourContainsMethod.invoke(getStdManager(), location, fo)).booleanValue();
}
catch (Throwable e) {
throw new UnsupportedOperationException(e);
}
return myContainsCall.callDefaultImpl(getStdManager(), "file object " + fo.getClass().getName(), location, fo);
}
private final DelegateCallHandler<JavaFileManager, Boolean> myContainsCall = new DelegateCallHandler<JavaFileManager, Boolean>(
JavaFileManager.class, "contains", Location.class, FileObject.class
);
public void onOutputFileGenerated(File file) {
final File parent = file.getParentFile();
@@ -530,6 +527,76 @@ public class JpsJavacFileManager extends ForwardingJavaFileManager<StandardJavaF
myOutputsMap = outputDirToSrcRoots;
}
private final DelegateCallHandler<StandardJavaFileManager, Void> mySetLocationForModuleCall = new DelegateCallHandler<StandardJavaFileManager, Void>(
StandardJavaFileManager.class, "setLocationForModule", Location.class, String.class, Collection.class
);
private final DelegateCallHandler<File, Object> myToPathCall = new DelegateCallHandler<File, Object>(File.class, "toPath");
private void initExplodedModuleNames(final Location modulePathLocation, Iterable<? extends File> path) throws IOException {
if (mySetLocationForModuleCall.isAvailable() && myToPathCall.isAvailable()) {
for (File pathEntry : path) {
final String explodedModuleName = myContext.getExplodedAutomaticModuleName(pathEntry);
if (explodedModuleName != null) {
mySetLocationForModuleCall.callDefaultImpl(
getStdManager(), modulePathLocation, explodedModuleName, Collections.singleton(myToPathCall.callDefaultImpl(pathEntry))
);
}
}
}
}
@SuppressWarnings("unchecked")
private static class DelegateCallHandler<T, R> {
private final Method myMethod;
private final String myUnsupportedMessage;
DelegateCallHandler(final Class<? extends T> apiInterface, String methodName, Class<?>... argTypes) {
myUnsupportedMessage = "Operation "+ methodName + " is not supported";
Method m = null;
try {
m = apiInterface.getDeclaredMethod(methodName, argTypes);
}
catch (Throwable ignored) {
}
myMethod = m;
}
boolean isAvailable() {
return myMethod != null;
}
R callDefaultImpl(final T callTarget, Object... args) throws IOException {
return callDefaultImpl(callTarget, "", args);
}
R callDefaultImpl(final T callTarget, String errorDetails, Object... args) throws IOException{
if (!isAvailable()) {
throw new UnsupportedOperationException(getErrorMessage(errorDetails));
}
// delegate the call further
try {
return (R)myMethod.invoke(callTarget, args);
}
catch (InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw (IOException)cause;
}
if (cause instanceof RuntimeException) {
throw (RuntimeException)cause;
}
throw new UnsupportedOperationException(getErrorMessage(errorDetails), cause != null ? cause : e);
}
catch (Throwable e) {
throw new UnsupportedOperationException(getErrorMessage(errorDetails), e);
}
}
private String getErrorMessage(String errorDetails) {
return errorDetails.isEmpty() ? myUnsupportedMessage : myUnsupportedMessage + ": " + errorDetails;
}
}
public static <T> Iterable<T> merge(final Iterable<? extends T> first, final Iterable<? extends T> second) {
return new Iterable<T>() {
@Override
@@ -0,0 +1,102 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.javac;
import gnu.trove.THashMap;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/**
* @author Eugene Zhuravlev
* Date: 17-Oct-19
*/
public abstract class ModulePath {
public interface Builder {
Builder add(String moduleName, File pathElement);
ModulePath create();
}
public abstract Collection<? extends File> getPath();
/**
* @param pathElement a single module path enntry
* @return a JPMS module name associated with the passed module path element.
* null value does not necessarily mean the entry cannot be treated as a JPMS module. null only
* means that there is no module name information stored for the file in this ModulePath object
*/
public abstract String getModuleName(File pathElement);
public boolean isEmpty() {
return getPath().isEmpty();
}
public static final ModulePath EMPTY = new ModulePath() {
@Override
public Collection<? extends File> getPath() {
return Collections.emptyList();
}
@Override
public String getModuleName(File pathElement) {
return null;
}
};
public static ModulePath create(Collection<? extends File> path) {
if (path.isEmpty()) {
return EMPTY;
}
final Collection<File> files = Collections.unmodifiableCollection(path);
return new ModulePath() {
@Override
public Collection<? extends File> getPath() {
return files;
}
@Override
public String getModuleName(File pathElement) {
return null;
}
};
}
public static Builder newBuilder() {
return new Builder() {
private final Map<File, String> myMap = new THashMap<File, String>();
private final Collection<File> myPath = new ArrayList<File>();
@Override
public Builder add(String moduleName, File pathElement) {
myPath.add(pathElement);
if (moduleName != null) {
myMap.put(pathElement, moduleName);
}
return this;
}
@Override
public ModulePath create() {
if (myPath.isEmpty()) {
return EMPTY;
}
final Collection<File> files = Collections.unmodifiableCollection(myPath);
return new ModulePath() {
@Override
public Collection<? extends File> getPath() {
return files;
}
@Override
public String getModuleName(File pathElement) {
return myMap.get(pathElement);
}
};
}
};
}
}
@@ -27,14 +27,10 @@ import org.jetbrains.jps.PathUtils;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.api.GlobalOptions;
import org.jetbrains.jps.backwardRefs.JavaBackwardReferenceIndexWriter;
import org.jetbrains.jps.builders.BuildRootIndex;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.FileProcessor;
import org.jetbrains.jps.builders.*;
import org.jetbrains.jps.builders.impl.DirtyFilesHolderBase;
import org.jetbrains.jps.builders.java.JavaBuilderExtension;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.JavaCompilingTool;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.builders.impl.TargetOutputIndexImpl;
import org.jetbrains.jps.builders.java.*;
import org.jetbrains.jps.builders.logging.ProjectBuilderLogger;
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
@@ -59,7 +55,8 @@ import org.jetbrains.jps.model.serialization.PathMacroUtil;
import org.jetbrains.jps.service.JpsServiceManager;
import org.jetbrains.jps.service.SharedThreadPool;
import javax.tools.*;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
@@ -70,6 +67,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.intellij.openapi.util.Pair.pair;
@@ -147,7 +145,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
if (LOG.isDebugEnabled()) {
LOG.debug("Java compiler ID: " + compilerId);
}
MODULE_PATH_SPLITTER.set(context, new ModulePathSplitter());
MODULE_PATH_SPLITTER.set(context, new ModulePathSplitter(new ExplodedModuleNameFinder(context)));
JavaCompilingTool compilingTool = JavaBuilderUtil.findCompilingTool(compilerId);
COMPILING_TOOL.set(context, compilingTool);
COMPILER_USAGE_STATISTICS.set(context, new ConcurrentHashMap<>());
@@ -443,23 +441,27 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
Collection<? extends File> classPath = originalClassPath;
Collection<File> modulePath = Collections.emptyList();
ModulePath modulePath = ModulePath.EMPTY;
Collection<? extends File> upgradeModulePath = Collections.emptyList();
if (moduleInfoFile != null) { // has modules
final ModulePathSplitter splitter = MODULE_PATH_SPLITTER.get(context);
final Pair<ModulePath, Collection<File>> pair = splitter.splitPath(
moduleInfoFile, outs.keySet(), ProjectPaths.getCompilationModulePath(chunk, false)
);
final boolean useModulePathOnly = Boolean.parseBoolean(System.getProperty(USE_MODULE_PATH_ONLY_OPTION))/*compilerConfig.useModulePathOnly()*/;
if (useModulePathOnly) {
// in Java 9, named modules are not allowed to read classes from the classpath
// moreover, the compiler requires all transitive dependencies to be on the module path
modulePath = ProjectPaths.getCompilationModulePath(chunk, false);
ModulePath.Builder mpBuilder = ModulePath.newBuilder();
for (File file : ProjectPaths.getCompilationModulePath(chunk, false)) {
mpBuilder.add(pair.first.getModuleName(file), file);
}
modulePath = mpBuilder.create();
classPath = Collections.emptyList();
}
else {
// placing only explicitly referenced modules into the module path and the rest of deps to classpath
final ModulePathSplitter splitter = MODULE_PATH_SPLITTER.get(context);
final Pair<Collection<File>, Collection<File>> pair = splitter.splitPath(
moduleInfoFile, outs.keySet(), ProjectPaths.getCompilationModulePath(chunk, false)
);
modulePath = pair.first;
classPath = pair.second;
}
@@ -1247,6 +1249,29 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
private static class ExplodedModuleNameFinder implements Function<File, String> {
private final TargetOutputIndex myOutsIndex;
ExplodedModuleNameFinder(CompileContext context) {
final BuildTargetIndex targetIndex = context.getProjectDescriptor().getBuildTargetIndex();
final List<ModuleBuildTarget> javaModuleTargets = new ArrayList<>();
for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) {
javaModuleTargets.addAll(targetIndex.getAllTargets(type));
}
myOutsIndex = new TargetOutputIndexImpl(javaModuleTargets, context);
}
@Override
public String apply(File outputDir) {
for (BuildTarget<?> target : myOutsIndex.getTargetsByOutputFile(outputDir)) {
if (target instanceof ModuleBasedTarget) {
return ((ModuleBasedTarget<?>)target).getModule().getName().trim();
}
}
return ModulePathSplitter.DEFAULT_MODULE_NAME_SEARCH.apply(outputDir);
}
}
private class ClassProcessingConsumer implements OutputFileConsumer {
private final CompileContext myContext;
private final OutputFileConsumer myDelegateOutputFileSink;
@@ -11,19 +11,27 @@ import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.javac.JpsJavacFileManager;
import org.jetbrains.jps.javac.ModulePath;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
/**
* @author Eugene Zhuravlev
* Date: 26-Sep-19
*/
public class ModulePathSplitter {
private final Map<File, ModuleInfo> myCache = Collections.synchronizedMap(new THashMap<>(FileUtil.FILE_HASHING_STRATEGY));
private static final Attributes.Name AUTOMATIC_MODULE_NAME = new Attributes.Name("Automatic-Module-Name");
private static final Method myModuleFinderCreateMethod;
private static final Method myFindAll;
private static final Method myGetDescriptor;
@@ -60,22 +68,72 @@ public class ModulePathSplitter {
myDescriptorRequires = descriptorRequires;
}
// derives module name from filename
public static final Function<File, String> DEFAULT_MODULE_NAME_SEARCH = file -> {
final String fName = file.getName();
final int dotIndex = fName.lastIndexOf('.'); // drop extension
return dotIndex >= 0? fName.substring(0, dotIndex) : fName;
};
@NotNull
private final Function<File, String> myModuleNameSearch;
public ModulePathSplitter() {
this(DEFAULT_MODULE_NAME_SEARCH);
}
public ModulePathSplitter(@NotNull Function<File, String> moduleNameSearch) {
myModuleNameSearch = moduleNameSearch;
}
public Pair<Collection<File>, Collection<File>> splitPath(File chunkModuleInfo, Set<File> chunkOutputs, Collection<File> path) {
public Pair<ModulePath, Collection<File>> splitPath(File chunkModuleInfo, Set<File> chunkOutputs, Collection<File> path) {
if (myModuleFinderCreateMethod == null) {
// the module API is not available
return Pair.create(path, Collections.emptyList());
return Pair.create(ModulePath.create(path), Collections.emptyList());
}
final List<File> modulePath = new ArrayList<>();
final ModulePath.Builder mpBuilder = ModulePath.newBuilder();
final List<File> classpath = new ArrayList<>();
final Set<String> allRequired = collectRequired(chunkModuleInfo, JpsJavacFileManager.filter(path, file -> !chunkOutputs.contains(file)));
for (File file : path) {
(chunkOutputs.contains(file) || allRequired.contains(getModuleInfo(file).name) ? modulePath : classpath).add(file);
if (chunkOutputs.contains(file)) {
mpBuilder.add(null, file);
}
else {
final ModuleInfo info = getModuleInfo(file);
if (allRequired.contains(info.name)) {
// storing only names for automatic modules in "exploded" form.
// for all other kinds of roots module-name is correctly determined by javac itself
mpBuilder.add(info.isAutomaticExploded? info.name : null, file);
}
else {
classpath.add(file);
}
}
}
return Pair.create(Collections.unmodifiableList(modulePath), Collections.unmodifiableList(classpath));
return Pair.create(mpBuilder.create(), Collections.unmodifiableList(classpath));
}
private static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
private static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
private static String normalizeModuleName(String fName) {
if (fName != null) {
// replace non-alphanumeric
fName = NON_ALPHANUM.matcher(fName).replaceAll(".");
// collapse repeating dots
fName = REPEATING_DOTS.matcher(fName).replaceAll(".");
// drop leading and trailing dots
final int len = fName.length();
if (len > 0) {
final int start = fName.startsWith(".") ? 1 : 0;
final int end = fName.endsWith(".") ? len - 1 : len;
if (start > 0 || end < len) {
fName = fName.substring(start, end);
}
}
}
return fName;
}
private Set<String> collectRequired(File chunkModuleInfo, Iterable<? extends File> path) {
@@ -104,21 +162,30 @@ public class ModulePathSplitter {
try {
Object mf = myModuleFinderCreateMethod.invoke(null, (Object)new Path[]{f.toPath()}); // ModuleFinder.of(f.toPath());
for (Object moduleRef : (Set<?>)myFindAll.invoke(mf)) { // mf.findAll()
final Object descriptor = myGetDescriptor.invoke(moduleRef); // moduleRef.descriptor()
final String moduleName = (String)myDescriptorName.invoke(descriptor); // descriptor.name();
final Set<?> requires = (Set<?>)myDescriptorRequires.invoke(descriptor); //descriptor.requires();
if (requires.isEmpty()) {
info = new ModuleInfo(moduleName);
}
else {
final Set<String> req = new HashSet<>();
for (Object require : requires) {
req.add((String)myRequiresName.invoke(require)/*require.name()*/);
final Set<?> moduleRefs = (Set<?>)myFindAll.invoke(mf); // mf.findAll()
if (!moduleRefs.isEmpty()) {
for (Object moduleRef : moduleRefs) {
final Object descriptor = myGetDescriptor.invoke(moduleRef); // moduleRef.descriptor()
final String moduleName = (String)myDescriptorName.invoke(descriptor); // descriptor.name();
final Set<?> requires = (Set<?>)myDescriptorRequires.invoke(descriptor); //descriptor.requires();
if (requires.isEmpty()) {
info = new ModuleInfo(moduleName, false);
}
info = new ModuleInfo(moduleName, req);
else {
final Set<String> req = new HashSet<>();
for (Object require : requires) {
req.add((String)myRequiresName.invoke(require)/*require.name()*/);
}
info = new ModuleInfo(moduleName, req);
}
break;
}
}
else {
final String explodedModuleName = deriveAutomaticModuleName(f);
if (explodedModuleName != null) {
info = new ModuleInfo(explodedModuleName, true);
}
break;
}
}
catch (Throwable ignored) {
@@ -127,21 +194,45 @@ public class ModulePathSplitter {
return info;
}
private static final class ModuleInfo {
static final ModuleInfo EMPTY = new ModuleInfo(null, Collections.emptyList());
private String deriveAutomaticModuleName(File dir) {
if (dir.isDirectory()) {
try {
final BufferedInputStream is = new BufferedInputStream(new FileInputStream(new File(dir, "META-INF/MANIFEST.MF")));
try {
final String name = new Manifest(is).getMainAttributes().getValue(AUTOMATIC_MODULE_NAME);
return name != null ? name : normalizeModuleName(myModuleNameSearch.apply(dir));
}
finally {
is.close();
}
}
catch (FileNotFoundException e) {
return normalizeModuleName(myModuleNameSearch.apply(dir)); // inferring the module name from the dir
}
catch (Throwable ignored) {
}
}
return null;
}
private static final class ModuleInfo {
static final ModuleInfo EMPTY = new ModuleInfo(null, false);
@Nullable
final String name;
@NotNull
final Collection<String> requires;
private final boolean isAutomaticExploded;
ModuleInfo(String name) {
this(name, Collections.emptyList());
ModuleInfo(@Nullable String name, boolean isAutomaticExploded) {
this.name = name;
this.requires = Collections.emptyList();
this.isAutomaticExploded = isAutomaticExploded;
}
ModuleInfo(@Nullable String name, @NotNull Collection<String> requires) {
this.name = name;
this.requires = Collections.unmodifiableCollection(requires);
this.isAutomaticExploded = false;
}
}
}
@@ -16,18 +16,18 @@ public class CompilationPaths {
private final Collection<File> myPlatformClasspath;
private final Collection<File> myClasspath;
private final Collection<File> myUpgradeModulePath;
private final Collection<File> myModulePath;
private final ModulePath myModulePath;
private final Collection<File> mySourcePath;
public CompilationPaths(Collection<? extends File> platformClasspath,
Collection<? extends File> classpath,
Collection<? extends File> upgradeModulePath,
Collection<? extends File> modulePath,
ModulePath modulePath,
Collection<? extends File> sourcePath) {
myPlatformClasspath = constCollection(platformClasspath);
myClasspath = constCollection(classpath);
myUpgradeModulePath = constCollection(upgradeModulePath);
myModulePath = constCollection(modulePath);
myModulePath = modulePath;
mySourcePath = constCollection(sourcePath);
}
@@ -51,7 +51,7 @@ public class CompilationPaths {
}
@NotNull
public Collection<File> getModulePath() {
public ModulePath getModulePath() {
return myModulePath;
}
@@ -60,66 +60,12 @@ public class CompilationPaths {
return mySourcePath;
}
public interface Builder {
CompilationPaths create();
Builder setPlatformClasspath(Collection<File> path);
Builder setClasspath(Collection<File> path);
Builder setUpgradeModulePath(Collection<File> path);
Builder setModulePath(Collection<File> path);
Builder setSourcePath(Collection<File> path);
}
public static CompilationPaths create(@Nullable Collection<? extends File> platformCp,
@Nullable Collection<? extends File> cp,
@Nullable Collection<? extends File> upgradeModCp,
@Nullable Collection<? extends File> modulePath,
@NotNull ModulePath modulePath,
@Nullable Collection<? extends File> sourcePath) {
return new CompilationPaths(platformCp, cp, upgradeModCp, modulePath, sourcePath);
}
public static Builder builder() {
return new Builder() {
private Collection<File> mySourcePath;
private Collection<File> myModulePath;
private Collection<File> myUpgradeModulePath;
private Collection<File> myClasspath;
private Collection<File> myPlatformCp;
@Override
public CompilationPaths create() {
return CompilationPaths.create(myPlatformCp, myClasspath, myUpgradeModulePath, myModulePath, mySourcePath);
}
@Override
public Builder setPlatformClasspath(Collection<File> path) {
myPlatformCp = path;
return this;
}
@Override
public Builder setClasspath(Collection<File> path) {
myClasspath = path;
return this;
}
@Override
public Builder setUpgradeModulePath(Collection<File> path) {
myUpgradeModulePath = path;
return this;
}
@Override
public Builder setModulePath(Collection<File> path) {
myModulePath = path;
return this;
}
@Override
public Builder setSourcePath(Collection<File> path) {
mySourcePath = path;
return this;
}
};
}
}
@@ -31,7 +31,7 @@ import org.jetbrains.jps.builders.java.JavaCompilingTool;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.incremental.GlobalContextKey;
import javax.tools.*;
import javax.tools.Diagnostic;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -135,7 +135,7 @@ public class ExternalJavacManager extends ProcessAdapter {
CanceledStatus cancelStatus) {
return forkJavac(
javaHome, heapSize, vmOptions, options,
CompilationPaths.create(platformCp, classpath, upgradeModulePath, modulePath, sourcePath),
CompilationPaths.create(platformCp, classpath, upgradeModulePath, ModulePath.create(modulePath), sourcePath),
files, outs, diagnosticSink, outputSink, compilingTool, cancelStatus, false
).get();
}
@@ -1,6 +1,7 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.ether;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.jps.ModuleChunk;
@@ -10,8 +11,10 @@ import org.jetbrains.jps.builders.CompileScopeTestBuilder;
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.incremental.java.ModulePathSplitter;
import org.jetbrains.jps.javac.ModulePath;
import org.jetbrains.jps.model.JpsModuleRootModificationUtil;
import org.jetbrains.jps.model.java.JpsJavaDependencyScope;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.library.JpsLibrary;
import org.jetbrains.jps.model.module.JpsModule;
@@ -31,8 +34,11 @@ public class Java9Test extends IncrementalTestCase {
@Override
protected boolean shouldRunTest() {
if (!SystemInfo.IS_AT_LEAST_JAVA9) {
System.out.println("Test '" + getTestName(false) + "' skipped because it requires at least java 9 runtime");
// java9 is not supported anymore and javac9 contains some problems that are already fixed in java10+
// It is expected that for JPMS-related features javac10 or better will be used
final JavaSdkVersion sdkVersion = JavaSdkVersion.fromVersionString(SystemInfo.JAVA_VERSION);
if (sdkVersion == null || !sdkVersion.isAtLeast(JavaSdkVersion.JDK_10)) {
System.out.println("Test '" + getTestName(false) + "' skipped because it requires at least java 10 runtime");
return false;
}
return super.shouldRunTest();
@@ -137,17 +143,41 @@ public class Java9Test extends IncrementalTestCase {
final ModulePathSplitter splitter = new ModulePathSplitter();
final Collection<File> dependencies = ProjectPaths.getCompilationModulePath(new ModuleChunk(Collections.singleton(target)), false);
final Pair<Collection<File>, Collection<File>> split = splitter.splitPath(moduleInfoPath, Collections.singleton(outputDir), dependencies);
final Collection<File> modulePath = split.first;
final Pair<ModulePath, Collection<File>> split = splitter.splitPath(moduleInfoPath, Collections.singleton(outputDir), dependencies);
final ModulePath modulePath = split.first;
final Collection<File> classpath = split.second;
assertEquals(4, modulePath.size());
assertTrue(modulePath.contains(outputDir));
assertTrue(modulePath.contains(new File(libDir, "module_lib_1.jar")));
assertTrue(modulePath.contains(new File(libDir, "module_lib_2.jar")));
assertTrue(modulePath.contains(new File(libDir, "module_lib_util.jar")));
final Collection<? extends File> modPath = modulePath.getPath();
assertEquals(4, modPath.size());
assertTrue(modPath.contains(outputDir));
assertTrue(modPath.contains(new File(libDir, "module_lib_1.jar")));
assertTrue(modPath.contains(new File(libDir, "module_lib_2.jar")));
assertTrue(modPath.contains(new File(libDir, "module_lib_util.jar")));
assertEquals(1, classpath.size());
assertTrue(classpath.contains(new File(libDir, "module_lib_3.jar")));
}
public void testExplodedAutoModule() {
setupInitialProject();
final Map<String, JpsModule> modules = setupModules();
assertEquals(2, modules.size());
assertTrue(modules.containsKey("A"));
assertTrue(modules.containsKey("B"));
doBuild(CompileScopeTestBuilder.rebuild().allModules()).assertSuccessful();
}
public void testExplodedAutoModuleWithManifest() {
setupInitialProject();
final Map<String, JpsModule> modules = setupModules();
JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(myProject).addResourcePattern("*.MF");
assertEquals(2, modules.size());
assertTrue(modules.containsKey("A"));
assertTrue(modules.containsKey("B"));
doBuild(CompileScopeTestBuilder.rebuild().allModules()).assertSuccessful();
}
}