diff --git a/common/com/twitter/intellij/pants/model/TargetAddressInfo.java b/common/com/twitter/intellij/pants/model/TargetAddressInfo.java index b69b7cb80..1e673ed64 100644 --- a/common/com/twitter/intellij/pants/model/TargetAddressInfo.java +++ b/common/com/twitter/intellij/pants/model/TargetAddressInfo.java @@ -3,6 +3,7 @@ package com.twitter.intellij.pants.model; +import com.google.common.annotations.VisibleForTesting; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,6 +80,7 @@ public String getInternalPantsTargetType() { return pants_target_type; } + @VisibleForTesting public void setPantsTargetType(@NotNull String type) { pants_target_type = type; } @@ -96,4 +98,6 @@ public boolean isPython() { public boolean isJarLibrary() { return StringUtil.equals("jar_library", getInternalPantsTargetType()); } + + public boolean isTargetAlias() { return pants_target_type != null && (pants_target_type.equals("alias") || pants_target_type.equals("target"));} } diff --git a/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraph.java b/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraph.java index 12e491b4d..915842c4b 100644 --- a/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraph.java +++ b/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraph.java @@ -4,11 +4,13 @@ package com.twitter.intellij.pants.service.project.model.graph; +import com.google.common.collect.Queues; import com.google.common.collect.Sets; import com.intellij.openapi.diagnostic.Logger; import com.twitter.intellij.pants.PantsException; import com.twitter.intellij.pants.service.project.model.TargetInfo; +import java.util.ArrayDeque; import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -89,18 +91,36 @@ public int getMaxDepth() { } } + private Set expandAliasTargets(Set initialTargets) { + Set results = Sets.newHashSet(); + ArrayDeque q = Queues.newArrayDeque(initialTargets); + while (!q.isEmpty()) { + BuildGraphNode curr = q.pop(); + if (curr.isAliasTarget()) { + q.addAll(curr.getDependencies()); + } + else { + results.add(curr); + } + } + return results; + } + // level 0 - target roots // level 1 - target roots + direct deps // ... public Set getNodesUpToLevel(int level) { // Holds the current scope of build graph. Set results = getTargetRoots(); + results.addAll(expandAliasTargets(results)); + for (int i = 0; i < level; i++) { Set dependencies = new HashSet<>(); for (BuildGraphNode node : results) { dependencies.addAll(node.getDependencies()); } results.addAll(dependencies); + results.addAll(expandAliasTargets(dependencies)); // All nodes are in, no need to iterate more. if (results.size() == allNodes.size()) { break; diff --git a/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraphNode.java b/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraphNode.java index f71afe816..8d1f78cea 100644 --- a/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraphNode.java +++ b/src/com/twitter/intellij/pants/service/project/model/graph/BuildGraphNode.java @@ -48,6 +48,10 @@ public boolean isTargetRoot() { return myTargetInfo.getAddressInfos().stream().anyMatch(TargetAddressInfo::isTargetRoot); } + public boolean isAliasTarget() { + return myTargetInfo.getAddressInfos().stream().anyMatch(TargetAddressInfo::isTargetAlias); + } + public void addDependency(BuildGraphNode node) { myDependencies.add(node); } diff --git a/tests/com/twitter/intellij/pants/service/project/model/graph/BuildGraphTest.java b/tests/com/twitter/intellij/pants/service/project/model/graph/BuildGraphTest.java index dacab99c8..3170a0cc0 100644 --- a/tests/com/twitter/intellij/pants/service/project/model/graph/BuildGraphTest.java +++ b/tests/com/twitter/intellij/pants/service/project/model/graph/BuildGraphTest.java @@ -104,14 +104,71 @@ public void testOrphan() throws Exception { } } - private void injectTargetInfo( + public void testTargetAliasExpansion1() { + // a -> b + injectTargetInfoWithInternalPantsTargetType(targets, "a", "source", "target", IS_TARGET_ROOT, Optional.empty()); + injectTargetInfoWithInternalPantsTargetType(targets, "b", "source", "java_library", !IS_TARGET_ROOT, Optional.of("a")); + // a (root) -> b, level 1 + assertEquals(1, new BuildGraph(targets).getMaxDepth()); + // because 'a' is an alias, so its deps will automatically get expanded if 'a' is included. + assertEquals(2, new BuildGraph(targets).getNodesUpToLevel(0).size()); + } + + public void testTargetAliasExpansion2() { + // a -> b + injectTargetInfoWithInternalPantsTargetType(targets, "a", "source", "alias", IS_TARGET_ROOT, Optional.empty()); + injectTargetInfoWithInternalPantsTargetType(targets, "b", "source", "java_library", !IS_TARGET_ROOT, Optional.of("a")); + // a (root) -> b, level 1 + assertEquals(1, new BuildGraph(targets).getMaxDepth()); + // because 'a' is an alias, so its deps will automatically get expanded if 'a' is included. + assertEquals(2, new BuildGraph(targets).getNodesUpToLevel(0).size()); + } + + public void testTargetAliasExpansion3() { + // a -> b + injectTargetInfoWithInternalPantsTargetType(targets, "a", "source", "java_library", IS_TARGET_ROOT, Optional.empty()); + injectTargetInfoWithInternalPantsTargetType(targets, "b", "source", "target", !IS_TARGET_ROOT, Optional.of("a")); + // a (root) -> b, level 1 + assertEquals(1, new BuildGraph(targets).getMaxDepth()); + assertEquals(1, new BuildGraph(targets).getNodesUpToLevel(0).size()); + assertEquals(2, new BuildGraph(targets).getNodesUpToLevel(1).size()); + } + + public void testTargetAliasExpansion4() { + // a -> b -> c + // b -> d + injectTargetInfoWithInternalPantsTargetType(targets, "a", "source", "java_library", IS_TARGET_ROOT, Optional.empty()); + injectTargetInfoWithInternalPantsTargetType(targets, "b", "source", "target", !IS_TARGET_ROOT, Optional.of("a")); + injectTargetInfoWithInternalPantsTargetType(targets, "c", "source", "java_library", !IS_TARGET_ROOT, Optional.of("b")); + injectTargetInfoWithInternalPantsTargetType(targets, "d", "source", "java_library", !IS_TARGET_ROOT, Optional.of("b")); + // a (root) -> b, level 1 + assertEquals(2, new BuildGraph(targets).getMaxDepth()); + // depth 0 only has target 'a' + assertEquals(1, new BuildGraph(targets).getNodesUpToLevel(0).size()); + // depth 1 initially has 'a' & 'b', but because 'b' is an alias, its deps 'c' & 'd' will be added' + assertEquals(4, new BuildGraph(targets).getNodesUpToLevel(1).size()); + } + + private void injectTargetInfoWithInternalPantsTargetType( + Map targets, + String targetAddress, + String targetType, + String internalPantsTargetType, + boolean is_target_root, + Optional dependee + ) { + TargetInfo info = injectTargetInfo(targets, targetAddress, targetType, is_target_root, dependee); + info.getAddressInfos().stream().forEach(s -> s.setPantsTargetType(internalPantsTargetType)); + } + + private TargetInfo injectTargetInfo( Map targets, String targetAddress, - String type, + String targetType, boolean is_target_root, Optional dependee ) { - TargetInfo source = TargetInfoTest.createTargetInfoWithTargetAddressInfo(type); + TargetInfo source = TargetInfoTest.createTargetInfoWithTargetAddressInfo(targetType); source.getAddressInfos().forEach(s -> s.setIsTargetRoot(is_target_root)); //getTarget here is actually getting dependencies :/ @@ -124,5 +181,6 @@ private void injectTargetInfo( } targets.put(targetAddress, source); + return source; } }