/** * @return Pair of dep-file rule key and the members of possibleDepFileSourcePaths that actually * appeared in the dep file * @throws IOException */ public Optional<Pair<RuleKey, ImmutableSet<SourcePath>>> build( Optional<ImmutableSet<SourcePath>> possibleDepFileSourcePaths) throws IOException { ImmutableSet<SourcePath> inputs = builder.getInputsSoFar(); ImmutableSet<SourcePath> depFileInputs = inputs; if (possibleDepFileSourcePaths.isPresent()) { // possibleDepFileSourcePaths is an ImmutableSortedSet which implements contains() via // binary search rather than via hashing. Thus taking the intersection/difference // is O(n*log(n)). Here, we make a hash-based copy of the set, so that intersection // will be reduced to O(N). ImmutableSet<SourcePath> possibleDepFileSourcePathsUnsorted = ImmutableSet.copyOf(possibleDepFileSourcePaths.get()); Sets.SetView<SourcePath> nonDepFileInputs = Sets.difference(inputs, possibleDepFileSourcePathsUnsorted); builder.addToRuleKey(nonDepFileInputs); depFileInputs = ImmutableSet.copyOf(Sets.intersection(inputs, possibleDepFileSourcePathsUnsorted)); } Optional<RuleKey> ruleKey = builder.build(); if (ruleKey.isPresent()) { return Optional.of(new Pair<>(ruleKey.get(), depFileInputs)); } else { return Optional.empty(); } }
public Optional<Pair<RuleKey, ImmutableSet<SourcePath>>> build( Optional<ImmutableSet<SourcePath>> possibleDepFileSourcePaths, ImmutableList<DependencyFileEntry> depFileEntries) throws IOException { // TODO(jkeljo): Make this use possibleDepFileSourcePaths all the time Iterable<SourcePath> inputsSoFar = builder.getIterableInputsSoFar(); // Dep file paths come as a sorted set, which does binary search instead of hashing for // lookups, so we re-create it as a hashset. ImmutableSet<SourcePath> fastPossibleSourcePaths = possibleDepFileSourcePaths.isPresent() ? ImmutableSet.copyOf(possibleDepFileSourcePaths.get()) : ImmutableSet.copyOf(inputsSoFar); ImmutableSet<DependencyFileEntry> depFileEntriesSet = ImmutableSet.copyOf(depFileEntries); final ImmutableSet.Builder<SourcePath> depFileSourcePathsBuilder = ImmutableSet.builder(); final ImmutableSet.Builder<DependencyFileEntry> filesAccountedFor = ImmutableSet.builder(); // Each input path falls into one of three categories: // 1) It's not covered by dep files, so we need to consider it part of the rule key // 2) It's covered by dep files and present in the dep file, so we consider it // part of the rule key // 3) It's covered by dep files but not present in the dep file, so we don't include it in // the rule key (the benefit of dep file support is that lots of things fall in this // category, so we can avoid rebuilds that would have happened with input-based rule keys) for (SourcePath input : inputsSoFar) { if (!fastPossibleSourcePaths.contains(input)) { // If this path is not a dep file path, then add it to the builder directly builder.addToRuleKey(input); } else { // If this input path is covered by the dep paths, check to see if it was declared // as a real dependency by the dep file entries DependencyFileEntry entry = DependencyFileEntry.fromSourcePath(input, pathResolver); if (depFileEntriesSet.contains(entry)) { builder.addToRuleKey(input); depFileSourcePathsBuilder.add(input); filesAccountedFor.add(entry); } } } Sets.SetView<DependencyFileEntry> filesUnaccountedFor = Sets.difference(depFileEntriesSet, filesAccountedFor.build()); // If we don't find actual inputs in one of the rules that corresponded to the input, this // likely means that the rule changed to no longer use the input. In this case we need to // throw a `NoSuchFileException` so that the build engine handles this as a signal that the // dep file rule key cannot be used. if (!filesUnaccountedFor.isEmpty()) { throw new NoSuchFileException( String.format( "%s: could not find any inputs matching the relative paths [%s]", rule.getBuildTarget(), Joiner.on(',').join(filesUnaccountedFor))); } Optional<RuleKey> ruleKey = builder.build(); if (ruleKey.isPresent()) { return Optional.of(new Pair<>(ruleKey.get(), depFileSourcePathsBuilder.build())); } else { return Optional.empty(); } }