// Copyright 2015 Pants project contributors (see CONTRIBUTORS.md). // Licensed under the Apache License, Version 2.0 (see LICENSE). package com.twitter.intellij.pants.service.project.resolver; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.ProjectKeys; import com.intellij.openapi.externalSystem.model.project.ContentRootData; import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.externalSystem.model.project.ProjectData; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.twitter.intellij.pants.model.PantsSourceType; import com.twitter.intellij.pants.service.PantsCompileOptionsExecutor; import com.twitter.intellij.pants.service.project.PantsResolverExtension; import com.twitter.intellij.pants.service.project.model.ContentRoot; import com.twitter.intellij.pants.service.project.model.ProjectInfo; import com.twitter.intellij.pants.service.project.model.TargetInfo; import com.twitter.intellij.pants.service.project.model.graph.BuildGraph; import com.twitter.intellij.pants.util.PantsConstants; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; public class PantsSourceRootsExtension implements PantsResolverExtension { private static String getSourceRootRegardingTargetType(@NotNull TargetInfo targetInfo, @NotNull ContentRoot root) { return doNotSupportPackagePrefixes(targetInfo) ? root.getPackageRoot() : root.getRawSourceRoot(); } private static boolean doNotSupportPackagePrefixes(@NotNull TargetInfo targetInfo) { return targetInfo.isPythonTarget() || PantsSourceType.isResource(targetInfo.getSourcesType()); } private static boolean hasAnAncestorRoot(@NotNull Collection<String> baseSourceRoots, @NotNull String root) { for (String sourceRoot : baseSourceRoots) { if (FileUtil.isAncestor(sourceRoot, root, false)) { return true; } } return false; } @Override public void resolve( @NotNull ProjectInfo projectInfo, @NotNull PantsCompileOptionsExecutor executor, @NotNull DataNode<ProjectData> projectDataNode, @NotNull Map<String, DataNode<ModuleData>> modules, @NotNull Optional<BuildGraph> buildGraph ) { for (Map.Entry<String, TargetInfo> entry : projectInfo.getSortedTargets()) { final String targetAddress = entry.getKey(); final TargetInfo targetInfo = entry.getValue(); if (!modules.containsKey(targetAddress)) { continue; } final DataNode<ModuleData> moduleDataNode = modules.get(targetAddress); createContentRoots(moduleDataNode, targetInfo); } } private void createContentRoots(@NotNull DataNode<ModuleData> moduleDataNode, @NotNull final TargetInfo targetInfo) { final Set<ContentRoot> roots = targetInfo.getRoots(); if (roots.isEmpty()) { return; } for (String baseRoot : findBaseRoots(targetInfo, roots)) { final ContentRootData contentRoot = new ContentRootData(PantsConstants.SYSTEM_ID, baseRoot); moduleDataNode.createChild(ProjectKeys.CONTENT_ROOT, contentRoot); for (ContentRoot sourceRoot : roots) { final String sourceRootPathToAdd = getSourceRootRegardingTargetType(targetInfo, sourceRoot); if (FileUtil.isAncestor(baseRoot, sourceRootPathToAdd, false)) { try { contentRoot.storePath( targetInfo.getSourcesType().toExternalSystemSourceType(), sourceRootPathToAdd, doNotSupportPackagePrefixes(targetInfo) ? null : sourceRoot.getPackagePrefix() ); } catch (IllegalArgumentException e) { LOG.warn(e); // todo(fkorotkov): log and investigate exceptions from ContentRootData.storePath(ContentRootData.java:94) } } } } } @NotNull private List<String> findBaseRoots(@NotNull final TargetInfo targetInfo, Set<ContentRoot> roots) { Set<String> allRoots = roots.stream() .map(root -> getSourceRootRegardingTargetType(targetInfo, root)) .collect(Collectors.toSet()); final List<String> sortedRoots = ContainerUtil.sorted( allRoots, StringUtil::naturalCompare ); final List<String> baseRoots = ContainerUtilRt.newArrayList(); for (String root : sortedRoots) { if (!hasAnAncestorRoot(baseRoots, root)) { baseRoots.add(root); } } return baseRoots; } }