blob: 1b96f11f93c26b3cca45fc97f7779d8065d7cd98 [file] [log] [blame]
/*
* Copyright (C) 2020 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 "TypeChecker.h"
#include <algorithm>
#include <string>
#include <unordered_map>
#include <android-base/strings.h>
#include "Common.h"
using android::base::Result;
using android::properties::ParsePropertyInfoFile;
using android::properties::PropertyInfoEntry;
namespace {
std::string SyspropTypeToContextType(const sysprop::Property& prop) {
switch (prop.type()) {
case sysprop::Integer:
case sysprop::Long:
return "int";
case sysprop::UInt:
case sysprop::ULong:
return "uint";
case sysprop::Double:
return "double";
case sysprop::Boolean:
return "bool";
case sysprop::Enum: {
// Sort both values and then join
auto prop_values = android::base::Split(prop.enum_values(), "|");
std::sort(prop_values.begin(), prop_values.end());
return "enum " + android::base::Join(prop_values, " ");
}
default:
// All other types (string, all kinds of list) should fall here
return "string";
}
}
bool IsCompatible(const sysprop::Property& prop, const std::string& ctx_type) {
if (prop.type() == sysprop::Enum) {
// special case: we need to first sort values and then compare
auto prop_values = ParseEnumValues(prop.enum_values());
std::sort(prop_values.begin(), prop_values.end());
// ctx_type must be "enum [value1] [value2] ..."
auto ctx_values = android::base::Split(ctx_type, " ");
if (ctx_values.empty() || ctx_values[0] != "enum") {
return false;
}
ctx_values.erase(ctx_values.begin());
std::sort(ctx_values.begin(), ctx_values.end());
return prop_values == ctx_values;
}
return SyspropTypeToContextType(prop) == ctx_type;
}
std::string GetTypeName(const sysprop::Property& prop) {
if (prop.type() == sysprop::Enum) {
return "Enum " + prop.enum_values();
}
return sysprop::Type_Name(prop.type());
}
} // namespace
Result<void> CheckPropertyTypes(const sysprop::SyspropLibraryApis& api,
const std::vector<PropertyInfoEntry>& entries) {
std::string err;
// map from exact property names to types in property_contexts
std::unordered_map<std::string, std::string> types;
for (auto& entry : entries) {
// skip prefix entries.
if (!entry.exact_match) continue;
// Duplicated prop check is intentionally skipped.
// Build will fail if any duplication happens.
types.emplace(entry.name, entry.type);
}
for (auto& props : api.props()) {
for (auto& prop : props.prop()) {
std::vector<std::string> prop_names{prop.prop_name()};
std::string legacy_name = prop.legacy_prop_name();
if (!legacy_name.empty()) prop_names.push_back(legacy_name);
for (auto& prop_name : prop_names) {
// Skip check if there is no exactly matched property.
auto itr = types.find(prop_name);
if (itr == types.end()) continue;
if (!IsCompatible(prop, itr->second)) {
if (!err.empty()) err += "\n";
err += "Type of prop '" + prop_name +
"' is incompatible with property_contexts\n";
err += "In sysprop_library: " + GetTypeName(prop) + "\n";
err += "In property_contexts: " + itr->second + " (should be '" +
SyspropTypeToContextType(prop) + "')\n";
}
}
}
}
if (err.empty())
return {};
else
return Errorf("{}", err);
}