public BottomUpPathMerger( Iterable<IjFolder> foldersToWalk, int limit, PackagePathCache packagePathCache) { this.tree = new MutableDirectedGraph<>(); this.packagePathCache = packagePathCache; this.mergePathsMap = new HashMap<>(); for (IjFolder folder : foldersToWalk) { mergePathsMap.put(folder.getPath(), folder); Path path = folder.getPath(); while (path.getNameCount() > limit) { Path parent = path.getParent(); if (parent == null) { break; } boolean isParentAndGrandParentAlreadyInTree = tree.containsNode(parent); tree.addEdge(parent, path); if (isParentAndGrandParentAlreadyInTree) { break; } path = parent; } } }
private DependencyGraph createDependencyGraphFromBuildRules(Iterable<? extends BuildRule> rules) { MutableDirectedGraph<BuildRule> graph = new MutableDirectedGraph<BuildRule>(); for (BuildRule rule : rules) { for (BuildRule dep : rule.getDeps()) { graph.addEdge(rule, dep); } } return new DependencyGraph(graph); }
/** Verify that owners are correctly detected: - one owner, multiple inputs, json output */ @Test public void verifyInputsWithOneOwnerAreCorrectlyReportedInJson() throws CmdLineException, IOException { // All files will be directories now FakeProjectFilesystem filesystem = new FakeProjectFilesystem() { @Override public File getFileForRelativePath(String pathRelativeToProjectRoot) { return new ExistingFile(getProjectRoot(), pathRelativeToProjectRoot); } }; // Create inputs String[] args = new String[] { "java/somefolder/badfolder/somefile.java", "java/somefolder/perfect.java", "com/test/subtest/random.java" }; ImmutableSortedSet<Path> inputs = MorePaths.asPaths(ImmutableSortedSet.copyOf(args)); // Build rule that owns all inputs BuildTarget target = new BuildTarget("//base/name", "name"); BuildRule ownerRule = new StubBuildRule(target, inputs); // Create graph MutableDirectedGraph<BuildRule> mutableGraph = new MutableDirectedGraph<BuildRule>(); mutableGraph.addNode(ownerRule); DependencyGraph graph = new DependencyGraph(mutableGraph); // Create options AuditOwnerOptions options = getOptions(args); // Create command under test AuditOwnerCommand command = createAuditOwnerCommand(filesystem); // Generate report and verify nonFileInputs are filled in as expected. AuditOwnerCommand.OwnersReport report = command.generateOwnersReport(graph, options); command.printOwnersOnlyJsonReport(report); String expectedJson = Joiner.on("") .join( "{", "\"com/test/subtest/random.java\":[\"//base/name:name\"],", "\"java/somefolder/badfolder/somefile.java\":[\"//base/name:name\"],", "\"java/somefolder/perfect.java\":[\"//base/name:name\"]", "}"); assertEquals(expectedJson, console.getTextWrittenToStdOut()); assertEquals("", console.getTextWrittenToStdErr()); }
/** Verify that owners are correctly detected: - inputs that belong to multiple targets */ @Test public void verifyInputsWithMultipleOwnersAreCorrectlyReported() throws CmdLineException { // All files will be directories now FakeProjectFilesystem filesystem = new FakeProjectFilesystem() { @Override public File getFileForRelativePath(String pathRelativeToProjectRoot) { return new ExistingFile(getProjectRoot(), pathRelativeToProjectRoot); } }; // Create inputs String[] args = new String[] { "java/somefolder/badfolder/somefile.java", "java/somefolder/perfect.java", "com/test/subtest/random.java" }; ImmutableSortedSet<InputRule> inputs = InputRule.inputPathsAsInputRules(ImmutableSortedSet.copyOf(args)); // Build rule that owns all inputs BuildTarget target1 = BuildTargetFactory.newInstance("//base/name1", "name1"); BuildTarget target2 = BuildTargetFactory.newInstance("//base/name2", "name2"); BuildRule owner1Rule = new StubBuildRule(target1, inputs); BuildRule owner2Rule = new StubBuildRule(target2, inputs); // Create graph MutableDirectedGraph<BuildRule> mutableGraph = new MutableDirectedGraph<BuildRule>(); mutableGraph.addNode(owner1Rule); mutableGraph.addNode(owner2Rule); DependencyGraph graph = new DependencyGraph(mutableGraph); // Create options AuditOwnerOptions options = getOptions(args); // Create command under test AuditOwnerCommand command = createAuditOwnerCommand(filesystem); // Generate report and verify nonFileInputs are filled in as expected. AuditOwnerCommand.OwnersReport report = command.generateOwnersReport(graph, options); assertTrue(report.nonFileInputs.isEmpty()); assertTrue(report.nonExistentInputs.isEmpty()); assertTrue(report.inputsWithNoOwners.isEmpty()); assertTrue(report.owners.containsKey(owner1Rule)); assertTrue(report.owners.containsKey(owner2Rule)); assertEquals(owner1Rule.getInputs(), report.owners.get(owner1Rule)); assertEquals(owner2Rule.getInputs(), report.owners.get(owner2Rule)); }
private PartialGraph createGraphFromBuildRules(List<BuildRule> rules) { MutableDirectedGraph<BuildRule> graph = new MutableDirectedGraph<>(); for (BuildRule rule : rules) { for (BuildRule dep : rule.getDeps()) { graph.addEdge(rule, dep); } } List<BuildTarget> buildTargets = Lists.transform( rules, new Function<BuildRule, BuildTarget>() { @Override public BuildTarget apply(BuildRule rule) { return rule.getBuildTarget(); } }); DependencyGraph dependencyGraph = new DependencyGraph(graph); return PartialGraphFactory.newInstance(dependencyGraph, buildTargets); }
/** * Walks the trie of paths attempting to merge all of the children of the current path into * itself. As soon as this fails we know we can't merge the parent path with the current path * either. * * @param currentPath current path * @return Optional.of(a successfully merged folder) or absent if merging did not succeed. */ private Optional<IjFolder> walk(Path currentPath) { ImmutableList<Optional<IjFolder>> children = StreamSupport.stream(tree.getOutgoingNodesFor(currentPath).spliterator(), false) .map(this::walk) .collect(MoreCollectors.toImmutableList()); List<IjFolder> presentChildren = new ArrayList<>(children.size()); for (Optional<IjFolder> folderOptional : children) { if (!folderOptional.isPresent()) { return Optional.empty(); } IjFolder folder = folderOptional.get(); // We don't want to merge exclude folders. if (folder instanceof ExcludeFolder) { continue; } presentChildren.add(folderOptional.get()); } IjFolder currentFolder = mergePathsMap.get(currentPath); if (presentChildren.isEmpty()) { return Optional.ofNullable(currentFolder); } final IjFolder mergeDistination; if (currentFolder != null) { mergeDistination = currentFolder; } else { mergeDistination = findBestChildToAggregateTo(presentChildren).createCopyWith(currentPath); } boolean allChildrenCanBeMerged = FluentIterable.from(presentChildren) .allMatch(input -> canMerge(mergeDistination, input, packagePathCache)); if (!allChildrenCanBeMerged) { return Optional.empty(); } return attemptMerge(mergeDistination, presentChildren); }
/** * Walks the trie of paths attempting to merge all of the children of the current path into * itself. As soon as this fails we know we can't merge the parent path with the current path * either. * * @param currentPath current path * @return Optional.of(a successfully merged folder) or absent if merging did not succeed. */ private Optional<IjFolder> walk(Path currentPath) { ImmutableList<Optional<IjFolder>> children = FluentIterable.from(tree.getOutgoingNodesFor(currentPath)) .transform( new Function<Path, Optional<IjFolder>>() { @Override public Optional<IjFolder> apply(Path input) { return walk(input); } }) .toList(); boolean anyAbsent = FluentIterable.from(children).anyMatch(Predicates.equalTo(Optional.<IjFolder>absent())); if (anyAbsent) { // Signal that no further merging should be done. return Optional.absent(); } ImmutableSet<IjFolder> presentChildren = FluentIterable.from(children) .transform( new Function<Optional<IjFolder>, IjFolder>() { @Override public IjFolder apply(Optional<IjFolder> input) { return input.get(); } }) .toSet(); IjFolder currentFolder = mergePathsMap.get(currentPath); if (presentChildren.isEmpty()) { return Optional.of(Preconditions.checkNotNull(currentFolder)); } final IjFolder myFolder; if (currentFolder != null) { myFolder = currentFolder; } else { IjFolder aChild = presentChildren.iterator().next(); myFolder = aChild.createCopyWith(currentPath); } boolean allChildrenCanBeMerged = FluentIterable.from(presentChildren) .allMatch( new Predicate<IjFolder>() { @Override public boolean apply(IjFolder input) { return canMerge(myFolder, input, packagePathCache); } }); if (!allChildrenCanBeMerged) { return Optional.absent(); } IjFolder mergedFolder = myFolder; for (IjFolder presentChild : presentChildren) { mergePathsMap.remove(presentChild.getPath()); mergedFolder = presentChild.merge(mergedFolder); } mergePathsMap.put(mergedFolder.getPath(), mergedFolder); return Optional.of(mergedFolder); }
public ImmutableSet<IjFolder> getMergedFolders() { for (Path topLevel : tree.getNodesWithNoIncomingEdges()) { walk(topLevel); } return ImmutableSet.copyOf(mergePathsMap.values()); }