• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetCppEnumName(const sysprop::Property & prop)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