• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <ostream>
19 #include <string>
20 #include <unordered_map>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include <android-base/logging.h>
25 
26 #include "common/libs/utils/result.h"
27 
28 namespace cuttlefish {
29 
30 template <typename Subclass>
31 class Feature {
32  public:
33   virtual ~Feature() = default;
34 
35   virtual std::string Name() const = 0;
36 
37   static Result<void> TopologicalVisit(
38       const std::unordered_set<Subclass*>& features,
39       const std::function<bool(Subclass*)>& callback);
40 
41  private:
42   virtual std::unordered_set<Subclass*> Dependencies() const = 0;
43 };
44 
45 class SetupFeature : public virtual Feature<SetupFeature> {
46  public:
47   virtual ~SetupFeature();
48 
49   static Result<void> RunSetup(const std::vector<SetupFeature*>& features);
50 
51   virtual bool Enabled() const = 0;
52 
53  private:
54   virtual Result<void> ResultSetup();
55   virtual bool Setup();
56 };
57 
58 class FlagFeature : public Feature<FlagFeature> {
59  public:
60   static Result<void> ProcessFlags(const std::vector<FlagFeature*>& features,
61                                    std::vector<std::string>& flags);
62   static bool WriteGflagsHelpXml(const std::vector<FlagFeature*>& features,
63                                  std::ostream& out);
64 
65  private:
66   // Must be executed in dependency order following Dependencies(). Expected to
67   // mutate the `flags` argument to remove handled flags, and possibly introduce
68   // new flag values (e.g. from a file).
69   virtual bool Process(std::vector<std::string>& flags) = 0;
70 
71   // TODO(schuffelen): Migrate the xml help to human-readable help output after
72   // the gflags migration is done.
73 
74   // Write an xml fragment that is compatible with gflags' `--helpxml` format.
75   virtual bool WriteGflagsCompatHelpXml(std::ostream& out) const = 0;
76 };
77 
78 template <typename Subclass>
TopologicalVisit(const std::unordered_set<Subclass * > & features,const std::function<bool (Subclass *)> & callback)79 Result<void> Feature<Subclass>::TopologicalVisit(
80     const std::unordered_set<Subclass*>& features,
81     const std::function<bool(Subclass*)>& callback) {
82   enum class Status { UNVISITED, VISITING, VISITED };
83   std::unordered_map<Subclass*, Status> features_status;
84   for (const auto& feature : features) {
85     features_status[feature] = Status::UNVISITED;
86   }
87   std::function<Result<void>(Subclass*)> visit;
88   visit = [&callback, &features_status,
89            &visit](Subclass* feature) -> Result<void> {
90     CF_EXPECT(features_status.count(feature) > 0,
91               "Dependency edge to "
92                   << feature->Name() << " but it is not part of the feature "
93                   << "graph. This feature is either disabled or not correctly "
94                   << "registered.");
95     if (features_status[feature] == Status::VISITED) {
96       return {};
97     }
98     CF_EXPECT(features_status[feature] != Status::VISITING,
99               "Cycle detected while visiting " << feature->Name());
100     features_status[feature] = Status::VISITING;
101     for (const auto& dependency : feature->Dependencies()) {
102       CF_EXPECT(dependency != nullptr,
103                 "SetupFeature " << feature->Name() << " has a null dependency.");
104       CF_EXPECT(visit(dependency),
105                 "Error detected while visiting " << feature->Name());
106     }
107     features_status[feature] = Status::VISITED;
108     CF_EXPECT(callback(feature), "Callback error on " << feature->Name());
109     return {};
110   };
111   for (const auto& feature : features) {
112     CF_EXPECT(visit(feature));  // `visit` will log the error chain.
113   }
114   return {};
115 }
116 
117 }  // namespace cuttlefish
118