243 lines
8.1 KiB
C++
243 lines
8.1 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "host/libs/config/config_flag.h"
|
|
|
|
#include <android-base/logging.h>
|
|
#include <android-base/strings.h>
|
|
#include <gflags/gflags.h>
|
|
#include <json/json.h>
|
|
#include <fstream>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
#include "common/libs/utils/files.h"
|
|
#include "common/libs/utils/flag_parser.h"
|
|
#include "host/libs/config/cuttlefish_config.h"
|
|
|
|
// To support other files that use this from gflags.
|
|
DEFINE_string(system_image_dir, "", "");
|
|
|
|
using gflags::FlagSettingMode::SET_FLAGS_DEFAULT;
|
|
|
|
namespace cuttlefish {
|
|
|
|
namespace {
|
|
|
|
class SystemImageDirFlagImpl : public SystemImageDirFlag {
|
|
public:
|
|
INJECT(SystemImageDirFlagImpl()) {
|
|
auto help = "Location of the system partition images.";
|
|
flag_ = GflagsCompatFlag("system_image_dir", path_).Help(help);
|
|
}
|
|
const std::string& Path() override { return path_; }
|
|
|
|
std::string Name() const override { return "SystemImageDirFlagImpl"; }
|
|
std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
|
|
bool Process(std::vector<std::string>& args) override {
|
|
path_ = DefaultGuestImagePath("");
|
|
if (!flag_.Parse(args)) {
|
|
return false;
|
|
}
|
|
// To support other files that use this from gflags.
|
|
FLAGS_system_image_dir = path_;
|
|
gflags::SetCommandLineOptionWithMode("system_image_dir", path_.c_str(),
|
|
SET_FLAGS_DEFAULT);
|
|
return true;
|
|
}
|
|
bool WriteGflagsCompatHelpXml(std::ostream&) const override {
|
|
// TODO(schuffelen): Write something here when this is removed from gflags
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
std::string path_;
|
|
Flag flag_;
|
|
};
|
|
|
|
class ConfigReader : public FlagFeature {
|
|
public:
|
|
INJECT(ConfigReader()) = default;
|
|
|
|
bool HasConfig(const std::string& name) const {
|
|
return allowed_config_presets_.count(name) > 0;
|
|
}
|
|
const std::set<std::string>& AvailableConfigs() const {
|
|
return allowed_config_presets_;
|
|
}
|
|
std::optional<Json::Value> ReadConfig(const std::string& name) const {
|
|
auto path =
|
|
DefaultHostArtifactsPath("etc/cvd_config/cvd_config_" + name + ".json");
|
|
Json::Value config;
|
|
Json::CharReaderBuilder builder;
|
|
std::ifstream ifs(path);
|
|
std::string errorMessage;
|
|
if (!Json::parseFromStream(builder, ifs, &config, &errorMessage)) {
|
|
LOG(ERROR) << "Could not read config file " << path << ": "
|
|
<< errorMessage;
|
|
return {};
|
|
}
|
|
return config;
|
|
}
|
|
|
|
// FlagFeature
|
|
std::string Name() const override { return "ConfigReader"; }
|
|
std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
|
|
bool Process(std::vector<std::string>&) override {
|
|
for (const std::string& file :
|
|
DirectoryContents(DefaultHostArtifactsPath("etc/cvd_config"))) {
|
|
std::string_view local_file(file);
|
|
if (android::base::ConsumePrefix(&local_file, "cvd_config_") &&
|
|
android::base::ConsumeSuffix(&local_file, ".json")) {
|
|
allowed_config_presets_.emplace(local_file);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
|
|
|
|
private:
|
|
std::set<std::string> allowed_config_presets_;
|
|
};
|
|
|
|
class ConfigFlagImpl : public ConfigFlag {
|
|
public:
|
|
INJECT(ConfigFlagImpl(ConfigReader& cr, SystemImageDirFlag& s))
|
|
: config_reader_(cr), system_image_dir_flag_(s) {
|
|
is_default_ = true;
|
|
config_ = "phone"; // default value
|
|
auto help =
|
|
"Config preset name. Will automatically set flag fields using the "
|
|
"values from this file of presets. See "
|
|
"device/google/cuttlefish/shared/config/config_*.json for possible "
|
|
"values.";
|
|
auto getter = [this]() { return config_; };
|
|
auto setter = [this](const FlagMatch& m) { return ChooseConfig(m.value); };
|
|
flag_ = GflagsCompatFlag("config").Help(help).Getter(getter).Setter(setter);
|
|
}
|
|
|
|
std::string Name() const override { return "ConfigFlagImpl"; }
|
|
std::unordered_set<FlagFeature*> Dependencies() const override {
|
|
return {
|
|
static_cast<FlagFeature*>(&config_reader_),
|
|
static_cast<FlagFeature*>(&system_image_dir_flag_),
|
|
};
|
|
}
|
|
bool Process(std::vector<std::string>& args) override {
|
|
if (!flag_.Parse(args)) {
|
|
LOG(ERROR) << "Failed to parse `--config` flag";
|
|
return false;
|
|
}
|
|
|
|
if (auto info_cfg = FindAndroidInfoConfig(); is_default_ && info_cfg) {
|
|
config_ = *info_cfg;
|
|
}
|
|
LOG(INFO) << "Launching CVD using --config='" << config_ << "'.";
|
|
auto config_values = config_reader_.ReadConfig(config_);
|
|
if (!config_values) {
|
|
LOG(ERROR) << "Failed to read config for " << config_;
|
|
return false;
|
|
}
|
|
for (const std::string& flag : config_values->getMemberNames()) {
|
|
std::string value;
|
|
if (flag == "custom_actions") {
|
|
Json::StreamWriterBuilder factory;
|
|
value = Json::writeString(factory, (*config_values)[flag]);
|
|
} else {
|
|
value = (*config_values)[flag].asString();
|
|
}
|
|
args.insert(args.begin(), "--" + flag + "=" + value);
|
|
// To avoid the flag forwarder from thinking this song is different from a
|
|
// default. Should fail silently if the flag doesn't exist.
|
|
gflags::SetCommandLineOptionWithMode(flag.c_str(), value.c_str(),
|
|
SET_FLAGS_DEFAULT);
|
|
}
|
|
return true;
|
|
}
|
|
bool WriteGflagsCompatHelpXml(std::ostream& out) const override {
|
|
return flag_.WriteGflagsCompatXml(out);
|
|
}
|
|
|
|
private:
|
|
bool ChooseConfig(const std::string& name) {
|
|
if (!config_reader_.HasConfig(name)) {
|
|
LOG(ERROR) << "Invalid --config option '" << name << "'. Valid options: "
|
|
<< android::base::Join(config_reader_.AvailableConfigs(), ",");
|
|
return false;
|
|
}
|
|
config_ = name;
|
|
is_default_ = false;
|
|
return true;
|
|
}
|
|
std::optional<std::string> FindAndroidInfoConfig() const {
|
|
auto info_path = system_image_dir_flag_.Path() + "/android-info.txt";
|
|
if (!FileExists(info_path)) {
|
|
return {};
|
|
}
|
|
std::ifstream ifs{info_path};
|
|
if (!ifs.is_open()) {
|
|
return {};
|
|
}
|
|
std::string android_info;
|
|
ifs >> android_info;
|
|
std::string_view local_android_info(android_info);
|
|
if (!android::base::ConsumePrefix(&local_android_info, "config=")) {
|
|
return {};
|
|
}
|
|
if (!config_reader_.HasConfig(std::string{local_android_info})) {
|
|
LOG(WARNING) << info_path << " contains invalid config preset: '"
|
|
<< local_android_info << "'.";
|
|
return {};
|
|
}
|
|
return std::string{local_android_info};
|
|
}
|
|
|
|
ConfigReader& config_reader_;
|
|
SystemImageDirFlag& system_image_dir_flag_;
|
|
std::string config_;
|
|
bool is_default_;
|
|
Flag flag_;
|
|
};
|
|
|
|
class ConfigFlagPlaceholderImpl : public ConfigFlag {
|
|
public:
|
|
INJECT(ConfigFlagPlaceholderImpl()) {}
|
|
|
|
std::string Name() const override { return "ConfigFlagPlaceholderImpl"; }
|
|
std::unordered_set<FlagFeature*> Dependencies() const override { return {}; }
|
|
bool Process(std::vector<std::string>&) override { return true; }
|
|
bool WriteGflagsCompatHelpXml(std::ostream&) const override { return true; }
|
|
};
|
|
|
|
} // namespace
|
|
|
|
fruit::Component<SystemImageDirFlag, ConfigFlag> ConfigFlagComponent() {
|
|
return fruit::createComponent()
|
|
.addMultibinding<FlagFeature, ConfigReader>()
|
|
.bind<ConfigFlag, ConfigFlagImpl>()
|
|
.addMultibinding<FlagFeature, ConfigFlag>()
|
|
.bind<SystemImageDirFlag, SystemImageDirFlagImpl>()
|
|
.addMultibinding<FlagFeature, SystemImageDirFlag>();
|
|
}
|
|
|
|
fruit::Component<ConfigFlag> ConfigFlagPlaceholder() {
|
|
return fruit::createComponent()
|
|
.addMultibinding<FlagFeature, ConfigFlag>()
|
|
.bind<ConfigFlag, ConfigFlagPlaceholderImpl>();
|
|
}
|
|
|
|
} // namespace cuttlefish
|