1 /* 2 * Copyright (C) 2018 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 17 #define LOG_TAG "sysprop_cpp_gen" 18 19 #include "CppGen.h" 20 21 #include <android-base/file.h> 22 #include <android-base/logging.h> 23 #include <android-base/stringprintf.h> 24 #include <android-base/strings.h> 25 #include <cerrno> 26 #include <filesystem> 27 #include <regex> 28 #include <string> 29 30 #include "CodeWriter.h" 31 #include "Common.h" 32 #include "sysprop.pb.h" 33 34 using android::base::Result; 35 36 namespace { 37 38 constexpr const char* kIndent = " "; 39 40 constexpr const char* kCppHeaderIncludes = 41 R"(#include <cstdint> 42 #include <optional> 43 #include <string> 44 #include <vector> 45 46 )"; 47 48 constexpr const char* kCppSourceIncludes = 49 R"(#include <cctype> 50 #include <cerrno> 51 #include <cstdio> 52 #include <cstring> 53 #include <limits> 54 #include <utility> 55 56 #include <strings.h> 57 #ifdef __BIONIC__ 58 #include <sys/system_properties.h> 59 [[maybe_unused]] static bool SetProp(const char* key, const char* value) { 60 return __system_property_set(key, value) == 0; 61 } 62 #else 63 #include <android-base/properties.h> 64 [[maybe_unused]] static bool SetProp(const char* key, const char* value) { 65 android::base::SetProperty(key, value); 66 return true; 67 } 68 #endif 69 70 #include <android-base/parseint.h> 71 #include <log/log.h> 72 73 )"; 74 75 constexpr const char* kCppParsersAndFormatters = 76 R"(template <typename T> constexpr bool is_vector = false; 77 78 template <typename T> constexpr bool is_vector<std::vector<T>> = true; 79 80 template <> [[maybe_unused]] std::optional<bool> DoParse(const char* str) { 81 static constexpr const char* kYes[] = {"1", "true"}; 82 static constexpr const char* kNo[] = {"0", "false"}; 83 84 for (const char* yes : kYes) { 85 if (strcasecmp(yes, str) == 0) return std::make_optional(true); 86 } 87 88 for (const char* no : kNo) { 89 if (strcasecmp(no, str) == 0) return std::make_optional(false); 90 } 91 92 return std::nullopt; 93 } 94 95 template <> [[maybe_unused]] std::optional<std::int32_t> DoParse(const char* str) { 96 std::int32_t ret; 97 return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt; 98 } 99 100 template <> [[maybe_unused]] std::optional<std::uint32_t> DoParse(const char* str) { 101 std::uint32_t ret; 102 return android::base::ParseUint(str, &ret) ? std::make_optional(ret) : std::nullopt; 103 } 104 105 template <> [[maybe_unused]] std::optional<std::int64_t> DoParse(const char* str) { 106 std::int64_t ret; 107 return android::base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt; 108 } 109 110 template <> [[maybe_unused]] std::optional<std::uint64_t> DoParse(const char* str) { 111 std::uint64_t ret; 112 return android::base::ParseUint(str, &ret) ? std::make_optional(ret) : std::nullopt; 113 } 114 115 template <> [[maybe_unused]] std::optional<double> DoParse(const char* str) { 116 int old_errno = errno; 117 errno = 0; 118 char* end; 119 double ret = std::strtod(str, &end); 120 if (errno != 0) { 121 return std::nullopt; 122 } 123 if (str == end || *end != '\0') { 124 errno = EINVAL; 125 return std::nullopt; 126 } 127 errno = old_errno; 128 return std::make_optional(ret); 129 } 130 131 template <> [[maybe_unused]] std::optional<std::string> DoParse(const char* str) { 132 return *str == '\0' ? std::nullopt : std::make_optional(str); 133 } 134 135 template <typename Vec> [[maybe_unused]] Vec DoParseList(const char* str) { 136 Vec ret; 137 if (*str == '\0') return ret; 138 const char* p = str; 139 for (;;) { 140 const char* r = p; 141 std::string value; 142 while (*r != ',') { 143 if (*r == '\\') ++r; 144 if (*r == '\0') break; 145 value += *r++; 146 } 147 ret.emplace_back(DoParse<typename Vec::value_type>(value.c_str())); 148 if (*r == '\0') break; 149 p = r + 1; 150 } 151 return ret; 152 } 153 154 template <typename T> inline T TryParse(const char* str) { 155 if constexpr(is_vector<T>) { 156 return DoParseList<T>(str); 157 } else { 158 return DoParse<T>(str); 159 } 160 } 161 162 [[maybe_unused]] std::string FormatValue(const std::optional<std::int32_t>& value) { 163 return value ? std::to_string(*value) : ""; 164 } 165 166 [[maybe_unused]] std::string FormatValue(const std::optional<std::uint32_t>& value) { 167 return value ? std::to_string(*value) : ""; 168 } 169 170 [[maybe_unused]] std::string FormatValue(const std::optional<std::int64_t>& value) { 171 return value ? std::to_string(*value) : ""; 172 } 173 174 [[maybe_unused]] std::string FormatValue(const std::optional<std::uint64_t>& value) { 175 return value ? std::to_string(*value) : ""; 176 } 177 178 [[maybe_unused]] std::string FormatValue(const std::optional<double>& value) { 179 if (!value) return ""; 180 char buf[1024]; 181 std::sprintf(buf, "%.*g", std::numeric_limits<double>::max_digits10, *value); 182 return buf; 183 } 184 185 [[maybe_unused]] std::string FormatValue(const std::optional<bool>& value) { 186 return value ? (*value ? "true" : "false") : ""; 187 } 188 189 template <typename T> 190 [[maybe_unused]] std::string FormatValue(const std::vector<T>& value) { 191 if (value.empty()) return ""; 192 193 std::string ret; 194 bool first = true; 195 196 for (auto&& element : value) { 197 if (!first) ret += ','; 198 else first = false; 199 if constexpr(std::is_same_v<T, std::optional<std::string>>) { 200 if (element) { 201 for (char c : *element) { 202 if (c == '\\' || c == ',') ret += '\\'; 203 ret += c; 204 } 205 } 206 } else { 207 ret += FormatValue(element); 208 } 209 } 210 211 return ret; 212 } 213 214 template <typename T> 215 T GetProp(const char* key, const char* legacy = nullptr) { 216 std::string value; 217 #ifdef __BIONIC__ 218 auto pi = __system_property_find(key); 219 if (pi != nullptr) { 220 __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) { 221 *static_cast<std::string*>(cookie) = value; 222 }, &value); 223 } 224 #else 225 value = android::base::GetProperty(key, ""); 226 #endif 227 if (value.empty() && legacy) { 228 ALOGV("prop %s doesn't exist; fallback to legacy prop %s", key, legacy); 229 return GetProp<T>(legacy); 230 } 231 return TryParse<T>(value.c_str()); 232 } 233 234 )"; 235 236 const std::regex kRegexDot{"\\."}; 237 const std::regex kRegexUnderscore{"_"}; 238 239 std::string GetCppEnumName(const sysprop::Property& prop); 240 std::string GetCppPropTypeName(const sysprop::Property& prop); 241 std::string GetCppNamespace(const sysprop::Properties& props); 242 243 std::string GenerateHeader(const sysprop::Properties& props, 244 sysprop::Scope scope); 245 std::string GenerateSource(const sysprop::Properties& props, 246 const std::string& include_name); 247 248 std::string GetCppEnumName(const sysprop::Property& prop) { 249 return ApiNameToIdentifier(prop.api_name()) + "_values"; 250 } 251 252 std::string GetCppPropTypeName(const sysprop::Property& prop) { 253 switch (prop.type()) { 254 case sysprop::Boolean: 255 return "std::optional<bool>"; 256 case sysprop::Integer: 257 return "std::optional<std::int32_t>"; 258 case sysprop::Long: 259 return "std::optional<std::int64_t>"; 260 case sysprop::Double: 261 return "std::optional<double>"; 262 case sysprop::String: 263 return "std::optional<std::string>"; 264 case sysprop::Enum: 265 return "std::optional<" + GetCppEnumName(prop) + ">"; 266 case sysprop::UInt: 267 return "std::optional<std::uint32_t>"; 268 case sysprop::ULong: 269 return "std::optional<std::uint64_t>"; 270 case sysprop::BooleanList: 271 return "std::vector<std::optional<bool>>"; 272 case sysprop::IntegerList: 273 return "std::vector<std::optional<std::int32_t>>"; 274 case sysprop::LongList: 275 return "std::vector<std::optional<std::int64_t>>"; 276 case sysprop::DoubleList: 277 return "std::vector<std::optional<double>>"; 278 case sysprop::StringList: 279 return "std::vector<std::optional<std::string>>"; 280 case sysprop::EnumList: 281 return "std::vector<std::optional<" + GetCppEnumName(prop) + ">>"; 282 case sysprop::UIntList: 283 return "std::vector<std::optional<std::uint32_t>>"; 284 case sysprop::ULongList: 285 return "std::vector<std::optional<std::uint64_t>>"; 286 default: 287 __builtin_unreachable(); 288 } 289 } 290 291 std::string GetCppNamespace(const sysprop::Properties& props) { 292 return std::regex_replace(props.module(), kRegexDot, "::"); 293 } 294 295 std::string GenerateHeader(const sysprop::Properties& props, 296 sysprop::Scope scope) { 297 CodeWriter writer(kIndent); 298 299 writer.Write("%s", kGeneratedFileFooterComments); 300 301 writer.Write("#pragma once\n\n"); 302 writer.Write("%s", kCppHeaderIncludes); 303 304 std::string cpp_namespace = GetCppNamespace(props); 305 writer.Write("namespace %s {\n\n", cpp_namespace.c_str()); 306 307 bool first = true; 308 309 for (int i = 0; i < props.prop_size(); ++i) { 310 const sysprop::Property& prop = props.prop(i); 311 312 // Scope: Internal > Public 313 if (prop.scope() > scope) continue; 314 315 if (!first) { 316 writer.Write("\n"); 317 } else { 318 first = false; 319 } 320 321 std::string prop_id = ApiNameToIdentifier(prop.api_name()); 322 std::string prop_type = GetCppPropTypeName(prop); 323 324 if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) { 325 writer.Write("enum class %s {\n", GetCppEnumName(prop).c_str()); 326 writer.Indent(); 327 for (const std::string& name : 328 android::base::Split(prop.enum_values(), "|")) { 329 writer.Write("%s,\n", ToUpper(name).c_str()); 330 } 331 writer.Dedent(); 332 writer.Write("};\n\n"); 333 } 334 335 if (prop.deprecated()) writer.Write("[[deprecated]] "); 336 writer.Write("%s %s();\n", prop_type.c_str(), prop_id.c_str()); 337 if (prop.access() != sysprop::Readonly) { 338 if (prop.deprecated()) writer.Write("[[deprecated]] "); 339 writer.Write("bool %s(const %s& value);\n", prop_id.c_str(), 340 prop_type.c_str()); 341 } 342 } 343 344 writer.Write("\n} // namespace %s\n", cpp_namespace.c_str()); 345 346 return writer.Code(); 347 } 348 349 std::string GenerateSource(const sysprop::Properties& props, 350 const std::string& include_name) { 351 CodeWriter writer(kIndent); 352 writer.Write("%s", kGeneratedFileFooterComments); 353 writer.Write("#include <%s>\n\n", include_name.c_str()); 354 writer.Write("%s", kCppSourceIncludes); 355 356 std::string cpp_namespace = GetCppNamespace(props); 357 358 writer.Write("namespace {\n\n"); 359 writer.Write("using namespace %s;\n\n", cpp_namespace.c_str()); 360 writer.Write("template <typename T> T DoParse(const char* str);\n\n"); 361 362 for (int i = 0; i < props.prop_size(); ++i) { 363 const sysprop::Property& prop = props.prop(i); 364 if (prop.type() != sysprop::Enum && prop.type() != sysprop::EnumList) { 365 continue; 366 } 367 368 std::string prop_id = ApiNameToIdentifier(prop.api_name()); 369 std::string enum_name = GetCppEnumName(prop); 370 371 writer.Write("constexpr const std::pair<const char*, %s> %s_list[] = {\n", 372 enum_name.c_str(), prop_id.c_str()); 373 writer.Indent(); 374 for (const std::string& name : ParseEnumValues(prop.enum_values())) { 375 writer.Write("{\"%s\", %s::%s},\n", name.c_str(), enum_name.c_str(), 376 ToUpper(name).c_str()); 377 } 378 writer.Dedent(); 379 writer.Write("};\n\n"); 380 381 writer.Write("template <>\n"); 382 writer.Write("std::optional<%s> DoParse(const char* str) {\n", 383 enum_name.c_str()); 384 writer.Indent(); 385 writer.Write("for (auto [name, val] : %s_list) {\n", prop_id.c_str()); 386 writer.Indent(); 387 writer.Write("if (strcmp(str, name) == 0) {\n"); 388 writer.Indent(); 389 writer.Write("return val;\n"); 390 writer.Dedent(); 391 writer.Write("}\n"); 392 writer.Dedent(); 393 writer.Write("}\n"); 394 writer.Write("return std::nullopt;\n"); 395 writer.Dedent(); 396 writer.Write("}\n\n"); 397 398 if (prop.access() != sysprop::Readonly) { 399 writer.Write("std::string FormatValue(std::optional<%s> value) {\n", 400 enum_name.c_str()); 401 writer.Indent(); 402 writer.Write("if (!value) return \"\";\n"); 403 writer.Write("for (auto [name, val] : %s_list) {\n", prop_id.c_str()); 404 writer.Indent(); 405 writer.Write("if (val == *value) {\n"); 406 writer.Indent(); 407 writer.Write("return name;\n"); 408 writer.Dedent(); 409 writer.Write("}\n"); 410 writer.Dedent(); 411 writer.Write("}\n"); 412 413 writer.Write( 414 "LOG_ALWAYS_FATAL(\"Invalid value %%d for property %s\", " 415 "static_cast<std::int32_t>(*value));\n", 416 prop.prop_name().c_str()); 417 418 writer.Write("__builtin_unreachable();\n"); 419 writer.Dedent(); 420 writer.Write("}\n\n"); 421 } 422 } 423 writer.Write("%s", kCppParsersAndFormatters); 424 writer.Write("} // namespace\n\n"); 425 426 writer.Write("namespace %s {\n\n", cpp_namespace.c_str()); 427 428 for (int i = 0; i < props.prop_size(); ++i) { 429 if (i > 0) writer.Write("\n"); 430 431 const sysprop::Property& prop = props.prop(i); 432 std::string prop_id = ApiNameToIdentifier(prop.api_name()); 433 std::string prop_type = GetCppPropTypeName(prop); 434 std::string prop_name = prop.prop_name(); 435 std::string legacy_name = prop.legacy_prop_name(); 436 437 writer.Write("%s %s() {\n", prop_type.c_str(), prop_id.c_str()); 438 writer.Indent(); 439 if (legacy_name.empty()) { 440 writer.Write("return GetProp<%s>(\"%s\");\n", prop_type.c_str(), 441 prop_name.c_str()); 442 } else { 443 writer.Write("return GetProp<%s>(\"%s\", \"%s\");\n", prop_type.c_str(), 444 prop_name.c_str(), legacy_name.c_str()); 445 } 446 writer.Dedent(); 447 writer.Write("}\n"); 448 449 if (prop.access() != sysprop::Readonly) { 450 writer.Write("\nbool %s(const %s& value) {\n", prop_id.c_str(), 451 prop_type.c_str()); 452 writer.Indent(); 453 454 const char* format_expr = "FormatValue(value).c_str()"; 455 456 // Specialized formatters here 457 if (prop.type() == sysprop::String) { 458 format_expr = "value ? value->c_str() : \"\""; 459 } else if (prop.integer_as_bool()) { 460 if (prop.type() == sysprop::Boolean) { 461 // optional<bool> -> optional<int> 462 format_expr = "FormatValue(std::optional<int>(value)).c_str()"; 463 } else if (prop.type() == sysprop::BooleanList) { 464 // vector<optional<bool>> -> vector<optional<int>> 465 format_expr = 466 "FormatValue(std::vector<std::optional<int>>(" 467 "value.begin(), value.end())).c_str()"; 468 } 469 } 470 471 writer.Write("return SetProp(\"%s\", %s);\n", prop.prop_name().c_str(), 472 format_expr); 473 writer.Dedent(); 474 writer.Write("}\n"); 475 } 476 } 477 478 writer.Write("\n} // namespace %s\n", cpp_namespace.c_str()); 479 480 return writer.Code(); 481 } 482 483 } // namespace 484 485 Result<void> GenerateCppFiles(const std::string& input_file_path, 486 const std::string& header_dir, 487 const std::string& public_header_dir, 488 const std::string& source_output_dir, 489 const std::string& include_name) { 490 sysprop::Properties props; 491 492 if (auto res = ParseProps(input_file_path); res.ok()) { 493 props = std::move(*res); 494 } else { 495 return res.error(); 496 } 497 498 std::string output_basename = android::base::Basename(input_file_path); 499 500 for (auto&& [scope, dir] : { 501 std::pair(sysprop::Internal, header_dir), 502 std::pair(sysprop::Public, public_header_dir), 503 }) { 504 std::error_code ec; 505 std::filesystem::create_directories(dir, ec); 506 if (ec) { 507 return Errorf("Creating directory to {} failed: {}", dir, ec.message()); 508 } 509 510 std::string path = dir + "/" + output_basename + ".h"; 511 std::string result = GenerateHeader(props, scope); 512 513 if (!android::base::WriteStringToFile(result, path)) { 514 return ErrnoErrorf("Writing generated header to {} failed", path); 515 } 516 } 517 518 std::string source_path = source_output_dir + "/" + output_basename + ".cpp"; 519 std::string source_result = GenerateSource(props, include_name); 520 521 if (!android::base::WriteStringToFile(source_result, source_path)) { 522 return ErrnoErrorf("Writing generated source to {} failed", source_path); 523 } 524 525 return {}; 526 } 527