Skip to content

Commit 799414a

Browse files
authored
Implement runtime flags codegen in C++ (#10711)
1 parent b8ba76e commit 799414a

File tree

3 files changed

+170
-88
lines changed

3 files changed

+170
-88
lines changed

ydb/core/base/generated/codegen/__main__.py

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include <util/stream/file.h>
2+
#include <util/stream/output.h>
3+
#include <util/string/builder.h>
4+
#include <util/system/src_location.h>
5+
#include <ydb/core/protos/feature_flags.pb.h>
6+
#include <google/protobuf/descriptor.pb.h>
7+
8+
#include <jinja2cpp/template_env.h>
9+
#include <jinja2cpp/template.h>
10+
#include <jinja2cpp/value.h>
11+
#include <jinja2cpp/reflected_value.h>
12+
#include <string>
13+
#include <vector>
14+
15+
struct TSlot;
16+
struct TField;
17+
18+
struct TSlot {
19+
TString Name;
20+
size_t Index;
21+
std::vector<const TField*> Fields;
22+
ui64 DefaultValue = 0;
23+
ui64 RuntimeFlagsMask = 0;
24+
25+
TSlot(const TString& name, size_t index)
26+
: Name(name)
27+
, Index(index)
28+
{}
29+
};
30+
31+
struct TField {
32+
TString Name;
33+
const TSlot* Slot;
34+
ui64 HasMask = 0;
35+
ui64 ValueMask = 0;
36+
ui64 FullMask = 0;
37+
ui64 DefaultValue = 0;
38+
bool IsRuntime = false;
39+
40+
TField(const TString& name, const TSlot* slot)
41+
: Name(name)
42+
, Slot(slot)
43+
{}
44+
};
45+
46+
namespace jinja2 {
47+
48+
template<>
49+
struct TypeReflection<TSlot> : TypeReflected<TSlot> {
50+
static const auto& GetAccessors() {
51+
static std::unordered_map<std::string, FieldAccessor> accessors = {
52+
{"name", [](const TSlot& slot) { return Reflect(std::string(slot.Name)); }},
53+
{"index", [](const TSlot& slot) { return Reflect(slot.Index); }},
54+
{"fields", [](const TSlot& slot) { return Reflect(slot.Fields); }},
55+
{"default_value", [](const TSlot& slot) {
56+
return Reflect(std::string(TStringBuilder() << slot.DefaultValue));
57+
}},
58+
{"runtime_flags_mask", [](const TSlot& slot) {
59+
return Reflect(std::string(TStringBuilder() << slot.RuntimeFlagsMask));
60+
}},
61+
};
62+
return accessors;
63+
}
64+
};
65+
66+
template<>
67+
struct TypeReflection<TField> : TypeReflected<TField> {
68+
static const auto& GetAccessors() {
69+
static std::unordered_map<std::string, FieldAccessor> accessors = {
70+
{"name", [](const TField& field) { return Reflect(std::string(field.Name)); }},
71+
{"slot", [](const TField& field) { return Reflect(field.Slot); }},
72+
{"has_mask", [](const TField& field) {
73+
return Reflect(std::string(TStringBuilder() << field.HasMask));
74+
}},
75+
{"value_mask", [](const TField& field) {
76+
return Reflect(std::string(TStringBuilder() << field.ValueMask));
77+
}},
78+
{"full_mask", [](const TField& field) {
79+
return Reflect(std::string(TStringBuilder() << field.FullMask));
80+
}},
81+
{"default_value", [](const TField& field) {
82+
return Reflect(std::string(TStringBuilder() << field.DefaultValue));
83+
}},
84+
{"is_runtime", [](const TField& field) { return Reflect(field.IsRuntime); }},
85+
};
86+
return accessors;
87+
}
88+
};
89+
90+
} // namespace jinja2
91+
92+
int main(int argc, char** argv) {
93+
if (argc < 3) {
94+
Cerr << "Usage: " << argv[0] << " INPUT OUTPUT ..." << Endl;
95+
return 1;
96+
}
97+
98+
std::deque<TSlot> slots;
99+
std::deque<TField> fields;
100+
std::vector<const TSlot*> jinjaSlots;
101+
std::vector<const TField*> jinjaFields;
102+
103+
TSlot* slot = nullptr;
104+
int currentBits = 0;
105+
106+
const auto* d = NKikimrConfig::TFeatureFlags::descriptor();
107+
for (int fieldIndex = 0; fieldIndex < d->field_count(); ++fieldIndex) {
108+
const auto* protoField = d->field(fieldIndex);
109+
if (protoField->type() != google::protobuf::FieldDescriptor::TYPE_BOOL) {
110+
continue;
111+
}
112+
if (!slot || currentBits + 2 > 64) {
113+
size_t index = slots.size();
114+
TString name = TStringBuilder() << "slot" << index;
115+
slot = &slots.emplace_back(name, index);
116+
currentBits = 0;
117+
jinjaSlots.push_back(slot);
118+
}
119+
int shift = currentBits;
120+
currentBits += 2;
121+
TField* field = &fields.emplace_back(protoField->name(), slot);
122+
jinjaFields.push_back(field);
123+
field->HasMask = 1ULL << shift;
124+
field->ValueMask = 2ULL << shift;
125+
field->FullMask = 3ULL << shift;
126+
if (protoField->default_value_bool()) {
127+
field->DefaultValue = field->ValueMask;
128+
}
129+
field->IsRuntime = !protoField->options().GetExtension(NKikimrConfig::RequireRestart);
130+
131+
slot->Fields.push_back(field);
132+
slot->DefaultValue |= field->DefaultValue;
133+
if (field->IsRuntime) {
134+
slot->RuntimeFlagsMask |= field->FullMask;
135+
}
136+
}
137+
138+
jinja2::TemplateEnv env;
139+
env.AddGlobal("generator", jinja2::Reflect(std::string(__SOURCE_FILE__)));
140+
env.AddGlobal("slots", jinja2::Reflect(jinjaSlots));
141+
env.AddGlobal("fields", jinja2::Reflect(jinjaFields));
142+
143+
for (int i = 1; i < argc; i += 2) {
144+
if (!(i + 1 < argc)) {
145+
Cerr << "ERROR: missing output for " << argv[i] << Endl;
146+
return 1;
147+
}
148+
149+
jinja2::Template t(&env);
150+
auto loaded = t.Load(TFileInput(argv[i]).ReadAll(), argv[i]);
151+
if (!loaded) {
152+
Cerr << "ERROR: " << loaded.error().ToString() << Endl;
153+
return 1;
154+
}
155+
156+
auto rendered = t.RenderAsString({});
157+
if (!rendered) {
158+
Cerr << "ERROR: " << rendered.error().ToString() << Endl;
159+
return 1;
160+
}
161+
162+
TFileOutput(argv[i + 1]).Write(rendered.value());
163+
Cout << "Generated " << argv[i + 1] << " from " << argv[i] << Endl;
164+
}
165+
166+
return 0;
167+
}

ydb/core/base/generated/codegen/ya.make

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
PY3_PROGRAM()
1+
PROGRAM()
22

3-
PY_SRCS(__main__.py)
3+
SRCS(main.cpp)
44

55
PEERDIR(
6-
contrib/python/MarkupSafe
7-
contrib/python/Jinja2
6+
contrib/libs/jinja2cpp
87
ydb/core/protos
98
)
109

0 commit comments

Comments
 (0)