/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "common/libs/utils/result.h" namespace cuttlefish { template class Feature { public: virtual ~Feature() = default; virtual std::string Name() const = 0; static Result TopologicalVisit( const std::unordered_set& features, const std::function& callback); private: virtual std::unordered_set Dependencies() const = 0; }; class SetupFeature : public virtual Feature { public: virtual ~SetupFeature(); static Result RunSetup(const std::vector& features); virtual bool Enabled() const = 0; private: virtual Result ResultSetup(); virtual bool Setup(); }; class FlagFeature : public Feature { public: static Result ProcessFlags(const std::vector& features, std::vector& flags); static bool WriteGflagsHelpXml(const std::vector& features, std::ostream& out); private: // Must be executed in dependency order following Dependencies(). Expected to // mutate the `flags` argument to remove handled flags, and possibly introduce // new flag values (e.g. from a file). virtual bool Process(std::vector& flags) = 0; // TODO(schuffelen): Migrate the xml help to human-readable help output after // the gflags migration is done. // Write an xml fragment that is compatible with gflags' `--helpxml` format. virtual bool WriteGflagsCompatHelpXml(std::ostream& out) const = 0; }; template Result Feature::TopologicalVisit( const std::unordered_set& features, const std::function& callback) { enum class Status { UNVISITED, VISITING, VISITED }; std::unordered_map features_status; for (const auto& feature : features) { features_status[feature] = Status::UNVISITED; } std::function(Subclass*)> visit; visit = [&callback, &features_status, &visit](Subclass* feature) -> Result { CF_EXPECT(features_status.count(feature) > 0, "Dependency edge to " << feature->Name() << " but it is not part of the feature " << "graph. This feature is either disabled or not correctly " << "registered."); if (features_status[feature] == Status::VISITED) { return {}; } CF_EXPECT(features_status[feature] != Status::VISITING, "Cycle detected while visiting " << feature->Name()); features_status[feature] = Status::VISITING; for (const auto& dependency : feature->Dependencies()) { CF_EXPECT(dependency != nullptr, "SetupFeature " << feature->Name() << " has a null dependency."); CF_EXPECT(visit(dependency), "Error detected while visiting " << feature->Name()); } features_status[feature] = Status::VISITED; CF_EXPECT(callback(feature), "Callback error on " << feature->Name()); return {}; }; for (const auto& feature : features) { CF_EXPECT(visit(feature)); // `visit` will log the error chain. } return {}; } } // namespace cuttlefish