Commit da71ef39 authored by Yuke Liao's avatar Yuke Liao Committed by Commit Bot

[GN] Implements analyzing dependencies between items.

The goal of this CL is to teach GN analyzer to handle build files.

Previous CL:
https://chromium-review.googlesource.com/c/chromium/src/+/838220
implemented tracking build dependency files for each item that
participates in the build dependency tree.

This CL implements analyzing the dependencies between items so as
to recursively decide the set of items/targets that are either
directly or indirectly affected by a list of modified source files
(including build files).

This CL also writes unit tests to verify the new behaviors.

Bug: 795913
Change-Id: I5c123180183246f719248415cdc496a154b5e754
Reviewed-on: https://chromium-review.googlesource.com/851080
Commit-Queue: Yuke Liao <liaoyuke@chromium.org>
Reviewed-by: default avatarDirk Pranke <dpranke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#527646}
parent 889c7e61
This diff is collapsed.
......@@ -10,9 +10,9 @@
#include <vector>
#include "tools/gn/builder.h"
#include "tools/gn/item.h"
#include "tools/gn/label.h"
#include "tools/gn/source_file.h"
#include "tools/gn/target.h"
// An Analyzer can answer questions about a build graph. It is used
// to answer queries for the `refs` and `analyze` commands, where we
......@@ -20,11 +20,10 @@
// from just a single Target.
class Analyzer {
public:
using LabelSet = std::set<Label>;
using SourceFileSet = std::set<const SourceFile*>;
using TargetSet = std::set<const Target*>;
explicit Analyzer(const Builder& builder);
Analyzer(const Builder& builder,
const SourceFile& build_config_file,
const SourceFile& dot_file,
const std::set<SourceFile>& build_args_dependency_files);
~Analyzer();
// Figures out from a Buider and a JSON-formatted string containing lists
......@@ -35,20 +34,17 @@ class Analyzer {
std::string Analyze(const std::string& input, Err* err) const;
private:
// Returns the roots of the build graph: the set of targets that
// no other target depends on.
TargetSet& roots() { return roots_; };
// Returns the set of all targets that might be affected, directly or
// Returns the set of all items that might be affected, directly or
// indirectly, by modifications to the given source files.
TargetSet AllAffectedTargets(const SourceFileSet& source_files) const;
std::set<const Item*> GetAllAffectedItems(
const std::set<const SourceFile*>& source_files) const;
// Returns the set of labels that do not refer to objects in the graph.
LabelSet InvalidLabels(const LabelSet& labels) const;
std::set<Label> InvalidLabels(const std::set<Label>& labels) const;
// Returns the set of all targets that have a label in the given set.
// Invalid (or missing) labels will be ignored.
TargetSet TargetsFor(const LabelSet& labels) const;
std::set<const Target*> TargetsFor(const std::set<Label>& labels) const;
// Returns a filtered set of the given targets, meaning that for each of the
// given targets,
......@@ -70,26 +66,39 @@ class Analyzer {
// ones).
//
// This filtering behavior is also known as "pruning" the list of targets.
TargetSet Filter(const TargetSet& targets) const;
std::set<const Target*> Filter(const std::set<const Target*>& targets) const;
// Filter an individual target and adds the results to filtered
// (see Filter(), above).
void FilterTarget(const Target*, TargetSet* seen, TargetSet* filtered) const;
void FilterTarget(const Target*,
std::set<const Target*>* seen,
std::set<const Target*>* filtered) const;
bool TargetRefersToFile(const Target* target, const SourceFile* file) const;
bool ItemRefersToFile(const Item* item, const SourceFile* file) const;
void AddTargetsDirectlyReferringToFileTo(const SourceFile* file,
TargetSet* matches) const;
void AddItemsDirectlyReferringToFile(
const SourceFile* file,
std::set<const Item*>* affected_items) const;
void AddAllRefsTo(const Target* target, TargetSet* matches) const;
void AddAllItemsReferringToItem(const Item* item,
std::set<const Item*>* affected_items) const;
std::vector<const Target*> all_targets_;
std::map<const Label, const Target*> labels_to_targets_;
// Main GN files stand for files whose context are used globally to execute
// every other build files, this list includes dot file, build config file,
// build args files etc.
bool WereMainGNFilesModified(
const std::set<const SourceFile*>& modified_files) const;
std::vector<const Item*> all_items_;
std::map<Label, const Item*> labels_to_items_;
Label default_toolchain_;
std::set<const Target*> roots_;
// Maps targets to the list of targets that depend on them.
std::multimap<const Target*, const Target*> dep_map_;
// Maps items to the list of items that depend on them.
std::multimap<const Item*, const Item*> dep_map_;
const SourceFile build_config_file_;
const SourceFile dot_file_;
const std::set<SourceFile> build_args_dependency_files_;
};
#endif // TOOLS_GN_ANALYZER_H_
This diff is collapsed.
......@@ -6,6 +6,7 @@
#include "base/sys_info.h"
#include "build/build_config.h"
#include "tools/gn/source_file.h"
#include "tools/gn/string_utils.h"
#include "tools/gn/variables.h"
......
......@@ -6,6 +6,7 @@
#define TOOLS_GN_ARGS_H_
#include <map>
#include <set>
#include "base/containers/hash_tables.h"
#include "base/macros.h"
......@@ -13,6 +14,7 @@
#include "tools/gn/scope.h"
class Err;
class SourceFile;
extern const char kBuildArgs_Help[];
......@@ -82,6 +84,17 @@ class Args {
// map instead of a hash map so the arguements are sorted alphabetically.
ValueWithOverrideMap GetAllArguments() const;
// Returns the set of build files that may affect the build arguments, please
// refer to Scope for how this is determined.
const std::set<SourceFile>& build_args_dependency_files() const {
return build_args_dependency_files_;
}
void set_build_args_dependency_files(
const std::set<SourceFile>& build_args_dependency_files) {
build_args_dependency_files_ = build_args_dependency_files;
}
private:
using ArgumentsPerToolchain =
base::hash_map<const Settings*, Scope::KeyValueMap>;
......@@ -126,6 +139,8 @@ class Args {
// we see an argument declaration.
mutable ArgumentsPerToolchain toolchain_overrides_;
std::set<SourceFile> build_args_dependency_files_;
DISALLOW_ASSIGN(Args);
};
......
......@@ -138,6 +138,19 @@ std::vector<const BuilderRecord*> Builder::GetAllRecords() const {
return result;
}
std::vector<const Item*> Builder::GetAllResolvedItems() const {
std::vector<const Item*> result;
result.reserve(records_.size());
for (const auto& record : records_) {
if (record.second->type() != BuilderRecord::ITEM_UNKNOWN &&
record.second->should_generate() && record.second->item()) {
result.push_back(record.second->item());
}
}
return result;
}
std::vector<const Target*> Builder::GetAllResolvedTargets() const {
std::vector<const Target*> result;
result.reserve(records_.size());
......
......@@ -45,6 +45,9 @@ class Builder {
std::vector<const BuilderRecord*> GetAllRecords() const;
// Returns items which should be generated and which are defined.
std::vector<const Item*> GetAllResolvedItems() const;
// Returns targets which should be generated and which are defined.
std::vector<const Target*> GetAllResolvedTargets() const;
......
......@@ -110,10 +110,12 @@ int RunAnalyze(const std::vector<std::string>& args) {
if (!setup->DoSetup(args[0], false) || !setup->Run())
return 1;
Analyzer analyzer(setup->builder());
Err err;
std::string output = Analyzer(setup->builder()).Analyze(input, &err);
Analyzer analyzer(
setup->builder(), setup->build_settings().build_config_file(),
setup->GetDotFile(),
setup->build_settings().build_args().build_args_dependency_files());
std::string output = analyzer.Analyze(input, &err);
if (err.has_error()) {
err.PrintToStdout();
return 1;
......
......@@ -61,6 +61,10 @@ class Item {
return build_dependency_files_;
}
std::set<SourceFile>& build_dependency_files() {
return build_dependency_files_;
}
// Called when this item is resolved, meaning it and all of its dependents
// have no unresolved deps. Returns true on success. Sets the error and
// returns false on failure.
......@@ -69,7 +73,7 @@ class Item {
private:
const Settings* settings_;
Label label_;
const std::set<SourceFile> build_dependency_files_;
std::set<SourceFile> build_dependency_files_;
const ParseNode* defined_from_;
Visibility visibility_;
......
......@@ -488,6 +488,8 @@ bool Setup::FillArgsFromArgsInputFile() {
Scope::KeyValueMap overrides;
arg_scope.GetCurrentScopeValues(&overrides);
build_settings_.build_args().AddArgOverrides(overrides);
build_settings_.build_args().set_build_args_dependency_files(
arg_scope.build_dependency_files());
return true;
}
......
......@@ -85,6 +85,8 @@ class Setup {
Builder& builder() { return builder_; }
LoaderImpl* loader() { return loader_.get(); }
const SourceFile& GetDotFile() const { return dotfile_input_file_->name(); }
// Name of the file in the root build directory that contains the build
// arguements.
static const char kBuildArgFileName[];
......
......@@ -27,6 +27,7 @@ class SourceFile {
// Takes a known absolute source file. Always begins in a slash.
explicit SourceFile(const base::StringPiece& p);
SourceFile(const SourceFile& other) = default;
// Constructs from the given string by swapping in the contents of the given
// value. The value will be the empty string after this call.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment