Adding Jar and Module dependencies
I am working on a plugin that handles dependencies in my project. At this point, I got something that kind of works, but it is very Frankenstein hacked together from trying things out and scowering this forum, as I have found the documentation to be very lacking. So I thought I would ask here if I added my dependencies in the right way.
My plugin has a method called "getClassPathForProject" this will return a list of JAR files that should be added as dependencies. One of the challenges is that this JAR file may be from another module in the project and if so I would like to add the Module as a dependency instead of the Jar file. I do this by mapping the root dirs of all my modules and see if the JAR file path starts with one of them. Currently, this works off a menu button.
public void actionPerformed(@NotNull final AnActionEvent e) {
Project project = e.getProject();
List<Module> modules = Arrays.asList(ModuleManager.getInstance(project).getModules());
for (Module m : modules) {
clearArchipelagoDependencies(project, m);
}
Map<Path, Module> map = getModulesMap(project);
for (Module m : modules) {
System.out.println("Adding dependencies for " + m.getName());
Path island = getPackageDir(m);
if (island != null) {
List<Path> classPaths = getClassPathForProject(island, m.getName().endsWith(".test"));
addDependency(e.getProject(), m, map, classPaths);
}
}
String[] options = new String[] {"Ok"};
Messages.showDialog("The dependencies has been resolved", "Dependency resolver", options,
0, Messages.getInformationIcon());
}
- clearArchipelagoDependencies - This will remove dependencies added before, so that we can start "fresh" when adding the dependencies.
- getModulesMap - This finds the root path for each module, this is used to figure out if i need to add a JAR or a module
- getClassPathForProject - This gets the JAR dependencies, may include JARS that should be a module dependency
private void addDependency(Project project, Module module, Map<Path, Module> map, List<Path> classPaths) {
LibraryTable projectLibraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(project);
final LibraryTable.ModifiableModel projectLibraryModel = projectLibraryTable.getModifiableModel();
for (Path path : classPaths) {
Optional<Path> modulePath = map.keySet().stream().filter(path::startsWith).findFirst();
if (modulePath.isPresent()) {
System.out.println("Dependency is a module: " + modulePath.get().getFileName());
ApplicationManager.getApplication().runWriteAction(() -> {
ModuleRootModificationUtil.addDependency(module, map.get(modulePath.get()));
});
continue;
}
// Not a Module add as JAR
final Library library = projectLibraryModel.createLibrary(ARCHIPELAGO_LIB_PREFIX + path.getFileName());
final Library.ModifiableModel libraryModel = library.getModifiableModel();
String pathUrl = VirtualFileManager.constructUrl(URLUtil.JAR_PROTOCOL, path.toString()) + JarFileSystem.JAR_SEPARATOR;
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(pathUrl);
if (file != null) {
System.out.println("Adding '" + file + "' as dependency");
libraryModel.addRoot(file, OrderRootType.CLASSES);
ApplicationManager.getApplication().runWriteAction(() -> {
libraryModel.commit();
projectLibraryModel.commit();
ModuleRootModificationUtil.addDependency(module, library, DependencyScope.COMPILE, true);
});
}
}
}
This took me a long time to fire out that i had to use "ModuleRootModificationUtil.addDependency" and i am not entirely sure that this is the right way. I am able to add a Prefix to the JAR dependencies, but not the Modules. If i were able to add that to modules dependencies it would make clean up a lot easier.
private void clearArchipelagoDependencies(Project project, Module module) {
System.out.println("");
System.out.println("");
System.out.println("Module: " + module.getName());
LibraryTable projectLibraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(project);
for (Library lib : projectLibraryTable.getLibraries()) {
if (lib.getName().startsWith("Gradle:")) {
continue;
}
if (lib.getName().startsWith(ARCHIPELAGO_LIB_PREFIX)) {
projectLibraryTable.removeLibrary(lib);
}
}
ApplicationManager.getApplication().runWriteAction(() -> {
ModifiableRootModel modifiableRootModel = ModuleRootManager.getInstance(module).getModifiableModel();
OrderEntry[] test = modifiableRootModel.getOrderEntries();
System.out.println("There are " + test.length + " OrderEntries");
for (ModuleOrderEntry entry : Arrays.stream(test)
.filter(e -> e instanceof ModuleOrderEntry)
.map(e -> (ModuleOrderEntry)e).collect(Collectors.toList())) {
System.out.println("Removing dependency on " + entry.getModuleName() + " from " + module.getName());
modifiableRootModel.removeOrderEntry(entry);
}
modifiableRootModel.commit();
});
}
Honestly, I am not really sure what I am doing here, but it seems to remove all dependencies. All JAR dependencies seem to be added to the LibraryTable, but Modules only to the "OrderEntrier"
private Map<Path, Module> getModulesMap(Project project) {
Map<Path, Module> map = new HashMap<>();
List<Module> modules = Arrays.asList(ModuleManager.getInstance(project).getModules());
for (Module m : modules) {
if (m.getName().endsWith(".test")) {
// We don't care about test modules here
continue;
}
ModuleRootManager mrm = ModuleRootManager.getInstance(m);
String[] dirs = mrm.getSourceRootUrls();
if (dirs.length == 0) {
continue;
}
String path = dirs[0].replace("file://", "");
Path island = getPackagePath(Paths.get(path));
map.put(island, m);
}
return map;
}
The way I get the module root dir seems a little hacky, is there really no better way to get the root dir of a module?
Please sign in to leave a comment.
Reference https://plugins.jetbrains.com/docs/intellij/module.html
Not sure what you mean with "root dir of a module".
I'm not really understanding why you operate with "Path" at all. All information about libraries and dependencies should be readily available in ModifiableRootModel. Could you please revisit existing code and post a new sample, showing sample project structure with specific questions?
Checking for "Gradle library" should be done using com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil#isExternalSystemLibrary passing org.jetbrains.plugins.gradle.util.GradleConstants#SYSTEM_ID.
The reason I am working with paths is that this is a dependency manager and has a CLI tool that I call to get a list of JARs that would be added as a dependency for that module. This CLI tool is not only for IntelliJ and it handles fetching the dependencies externally. So it provides the JAR links to me and I have to add that in IntelliJ as dependencies.