blob: 530af49866fb6858e9ef91b8e879193eafbc1857 [file] [log] [blame]
Dennis Shen1dc9ad42023-05-12 00:21:55 +00001/*
2 * Copyright (C) 2023 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
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020017use anyhow::{ensure, Result};
Dennis Shen1dc9ad42023-05-12 00:21:55 +000018use serde::Serialize;
Dennis Shen8d544f72023-06-29 00:45:42 +000019use std::path::PathBuf;
Dennis Shen1dc9ad42023-05-12 00:21:55 +000020use tinytemplate::TinyTemplate;
21
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020022use crate::codegen;
Dennis Shen8d544f72023-06-29 00:45:42 +000023use crate::commands::{CodegenMode, OutputFile};
Mårten Kongstad403658f2023-06-14 09:51:56 +020024use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
Dennis Shen1dc9ad42023-05-12 00:21:55 +000025
Dennis Shen8d544f72023-06-29 00:45:42 +000026pub fn generate_cpp_code<'a, I>(
27 package: &str,
28 parsed_flags_iter: I,
29 codegen_mode: CodegenMode,
30) -> Result<Vec<OutputFile>>
Mårten Kongstad403658f2023-06-14 09:51:56 +020031where
32 I: Iterator<Item = &'a ProtoParsedFlag>,
33{
Mårten Kongstad066575b2023-06-07 16:29:25 +020034 let class_elements: Vec<ClassElement> =
Mårten Kongstad403658f2023-06-14 09:51:56 +020035 parsed_flags_iter.map(|pf| create_class_element(package, pf)).collect();
Dennis Shen1dc9ad42023-05-12 00:21:55 +000036 let readwrite = class_elements.iter().any(|item| item.readwrite);
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020037 let header = package.replace('.', "_");
38 let cpp_namespace = package.replace('.', "::");
39 ensure!(codegen::is_valid_name_ident(&header));
Mårten Kongstad066575b2023-06-07 16:29:25 +020040 let context = Context {
41 header: header.clone(),
42 cpp_namespace,
43 package: package.to_string(),
44 readwrite,
Dennis Shen17a08eec2023-07-14 01:32:50 +000045 for_test: codegen_mode == CodegenMode::Test,
Mårten Kongstad066575b2023-06-07 16:29:25 +020046 class_elements,
47 };
Dennis Shen8d544f72023-06-29 00:45:42 +000048
49 let files = [
50 FileSpec {
51 name: &format!("{}.h", header),
52 template: include_str!("../templates/cpp_exported_header.template"),
53 dir: "include",
54 },
55 FileSpec {
56 name: &format!("{}.cc", header),
57 template: include_str!("../templates/cpp_source_file.template"),
58 dir: "",
59 },
Dennis Shen8d544f72023-06-29 00:45:42 +000060 ];
61 files.iter().map(|file| generate_file(file, &context)).collect()
62}
63
64pub fn generate_file(file: &FileSpec, context: &Context) -> Result<OutputFile> {
Dennis Shen1dc9ad42023-05-12 00:21:55 +000065 let mut template = TinyTemplate::new();
Dennis Shen8d544f72023-06-29 00:45:42 +000066 template.add_template(file.name, file.template)?;
67 let contents = template.render(file.name, &context)?;
68 let path: PathBuf = [&file.dir, &file.name].iter().collect();
Dennis Shen1dc9ad42023-05-12 00:21:55 +000069 Ok(OutputFile { contents: contents.into(), path })
70}
71
72#[derive(Serialize)]
Dennis Shen8d544f72023-06-29 00:45:42 +000073pub struct FileSpec<'a> {
74 pub name: &'a str,
75 pub template: &'a str,
76 pub dir: &'a str,
77}
78
79#[derive(Serialize)]
80pub struct Context {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +020081 pub header: String,
82 pub cpp_namespace: String,
Mårten Kongstad9fb58962023-05-31 13:02:13 +020083 pub package: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +000084 pub readwrite: bool,
Dennis Shen17a08eec2023-07-14 01:32:50 +000085 pub for_test: bool,
Dennis Shen1dc9ad42023-05-12 00:21:55 +000086 pub class_elements: Vec<ClassElement>,
87}
88
89#[derive(Serialize)]
Dennis Shen8d544f72023-06-29 00:45:42 +000090pub struct ClassElement {
Dennis Shen1dc9ad42023-05-12 00:21:55 +000091 pub readwrite: bool,
92 pub default_value: String,
93 pub flag_name: String,
Mårten Kongstad066575b2023-06-07 16:29:25 +020094 pub device_config_namespace: String,
95 pub device_config_flag: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +000096}
97
Mårten Kongstad403658f2023-06-14 09:51:56 +020098fn create_class_element(package: &str, pf: &ProtoParsedFlag) -> ClassElement {
Dennis Shen1dc9ad42023-05-12 00:21:55 +000099 ClassElement {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200100 readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,
101 default_value: if pf.state() == ProtoFlagState::ENABLED {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000102 "true".to_string()
103 } else {
104 "false".to_string()
105 },
Mårten Kongstad403658f2023-06-14 09:51:56 +0200106 flag_name: pf.name().to_string(),
107 device_config_namespace: pf.namespace().to_string(),
108 device_config_flag: codegen::create_device_config_ident(package, pf.name())
109 .expect("values checked at flag parse time"),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
Dennis Shen8d544f72023-06-29 00:45:42 +0000116 use std::collections::HashMap;
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000117
Dennis Shen8d544f72023-06-29 00:45:42 +0000118 const EXPORTED_PROD_HEADER_EXPECTED: &str = r#"
Dennis Shen5c242132023-07-14 14:57:08 +0000119#pragma once
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000120
Joe Onoratoac692c52023-07-18 17:29:14 -0700121#ifdef __cplusplus
122
Dennis Shen8d544f72023-06-29 00:45:42 +0000123#include <memory>
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000124
Mårten Kongstad403658f2023-06-14 09:51:56 +0200125namespace com::android::aconfig::test {
Dennis Shen8d544f72023-06-29 00:45:42 +0000126class flag_provider_interface {
127public:
Dennis Shen8d544f72023-06-29 00:45:42 +0000128 virtual ~flag_provider_interface() = default;
129
130 virtual bool disabled_ro() = 0;
131
132 virtual bool disabled_rw() = 0;
133
134 virtual bool enabled_ro() = 0;
135
136 virtual bool enabled_rw() = 0;
Dennis Shen8d544f72023-06-29 00:45:42 +0000137};
138
139extern std::unique_ptr<flag_provider_interface> provider_;
140
Dennis Shen8d544f72023-06-29 00:45:42 +0000141inline bool disabled_ro() {
142 return false;
143}
144
145inline bool disabled_rw() {
146 return provider_->disabled_rw();
147}
148
149inline bool enabled_ro() {
150 return true;
151}
152
153inline bool enabled_rw() {
154 return provider_->enabled_rw();
155}
156
Dennis Shen8d544f72023-06-29 00:45:42 +0000157}
Joe Onoratoac692c52023-07-18 17:29:14 -0700158
159extern "C" {
160#endif // __cplusplus
161
162bool com_android_aconfig_test_disabled_ro();
163
164bool com_android_aconfig_test_disabled_rw();
165
166bool com_android_aconfig_test_enabled_ro();
167
168bool com_android_aconfig_test_enabled_rw();
169
170#ifdef __cplusplus
171} // extern "C"
172#endif
Dennis Shen8d544f72023-06-29 00:45:42 +0000173"#;
174
175 const EXPORTED_TEST_HEADER_EXPECTED: &str = r#"
Dennis Shen5c242132023-07-14 14:57:08 +0000176#pragma once
Dennis Shen8d544f72023-06-29 00:45:42 +0000177
Joe Onoratoac692c52023-07-18 17:29:14 -0700178#ifdef __cplusplus
179
Dennis Shen8d544f72023-06-29 00:45:42 +0000180#include <memory>
Dennis Shen8d544f72023-06-29 00:45:42 +0000181
182namespace com::android::aconfig::test {
Joe Onoratoac692c52023-07-18 17:29:14 -0700183
Dennis Shen8d544f72023-06-29 00:45:42 +0000184class flag_provider_interface {
185public:
186
187 virtual ~flag_provider_interface() = default;
188
189 virtual bool disabled_ro() = 0;
190
Dennis Shen17a08eec2023-07-14 01:32:50 +0000191 virtual void disabled_ro(bool val) = 0;
192
Dennis Shen8d544f72023-06-29 00:45:42 +0000193 virtual bool disabled_rw() = 0;
194
Dennis Shen17a08eec2023-07-14 01:32:50 +0000195 virtual void disabled_rw(bool val) = 0;
196
Dennis Shen8d544f72023-06-29 00:45:42 +0000197 virtual bool enabled_ro() = 0;
198
Dennis Shen17a08eec2023-07-14 01:32:50 +0000199 virtual void enabled_ro(bool val) = 0;
200
Dennis Shen8d544f72023-06-29 00:45:42 +0000201 virtual bool enabled_rw() = 0;
202
Dennis Shen17a08eec2023-07-14 01:32:50 +0000203 virtual void enabled_rw(bool val) = 0;
Dennis Shen8d544f72023-06-29 00:45:42 +0000204
Dennis Shen17a08eec2023-07-14 01:32:50 +0000205 virtual void reset_flags() {}
Dennis Shen8d544f72023-06-29 00:45:42 +0000206};
207
208extern std::unique_ptr<flag_provider_interface> provider_;
209
Dennis Shen8d544f72023-06-29 00:45:42 +0000210inline bool disabled_ro() {
211 return provider_->disabled_ro();
212}
213
Dennis Shen17a08eec2023-07-14 01:32:50 +0000214inline void disabled_ro(bool val) {
215 provider_->disabled_ro(val);
216}
217
Dennis Shen8d544f72023-06-29 00:45:42 +0000218inline bool disabled_rw() {
219 return provider_->disabled_rw();
220}
221
Dennis Shen17a08eec2023-07-14 01:32:50 +0000222inline void disabled_rw(bool val) {
223 provider_->disabled_rw(val);
224}
225
Dennis Shen8d544f72023-06-29 00:45:42 +0000226inline bool enabled_ro() {
227 return provider_->enabled_ro();
228}
229
Dennis Shen17a08eec2023-07-14 01:32:50 +0000230inline void enabled_ro(bool val) {
231 provider_->enabled_ro(val);
232}
233
Dennis Shen8d544f72023-06-29 00:45:42 +0000234inline bool enabled_rw() {
235 return provider_->enabled_rw();
236}
237
Dennis Shen17a08eec2023-07-14 01:32:50 +0000238inline void enabled_rw(bool val) {
239 provider_->enabled_rw(val);
Dennis Shen8d544f72023-06-29 00:45:42 +0000240}
241
Dennis Shen17a08eec2023-07-14 01:32:50 +0000242inline void reset_flags() {
243 return provider_->reset_flags();
Dennis Shen8d544f72023-06-29 00:45:42 +0000244}
245
246}
Dennis Shen8d544f72023-06-29 00:45:42 +0000247
Dennis Shen7321f4f2023-07-11 15:45:00 +0000248extern "C" {
Joe Onoratoac692c52023-07-18 17:29:14 -0700249#endif // __cplusplus
Dennis Shen17a08eec2023-07-14 01:32:50 +0000250
251bool com_android_aconfig_test_disabled_ro();
252
253void set_com_android_aconfig_test_disabled_ro(bool val);
254
255bool com_android_aconfig_test_disabled_rw();
256
257void set_com_android_aconfig_test_disabled_rw(bool val);
258
259bool com_android_aconfig_test_enabled_ro();
260
261void set_com_android_aconfig_test_enabled_ro(bool val);
262
263bool com_android_aconfig_test_enabled_rw();
264
265void set_com_android_aconfig_test_enabled_rw(bool val);
266
267void com_android_aconfig_test_reset_flags();
Dennis Shen7321f4f2023-07-11 15:45:00 +0000268
Joe Onoratoac692c52023-07-18 17:29:14 -0700269
Dennis Shen7321f4f2023-07-11 15:45:00 +0000270#ifdef __cplusplus
Joe Onoratoac692c52023-07-18 17:29:14 -0700271} // extern "C"
Dennis Shen7321f4f2023-07-11 15:45:00 +0000272#endif
Joe Onoratoac692c52023-07-18 17:29:14 -0700273
274
Dennis Shen7321f4f2023-07-11 15:45:00 +0000275"#;
276
Joe Onoratoac692c52023-07-18 17:29:14 -0700277 const PROD_SOURCE_FILE_EXPECTED: &str = r#"
Dennis Shen7321f4f2023-07-11 15:45:00 +0000278#include "com_android_aconfig_test.h"
Joe Onoratoac692c52023-07-18 17:29:14 -0700279#include <server_configurable_flags/get_flags.h>
280
281namespace com::android::aconfig::test {
282
283 class flag_provider : public flag_provider_interface {
284 public:
285
286 virtual bool disabled_ro() override {
287 return false;
288 }
289
290 virtual bool disabled_rw() override {
291 return server_configurable_flags::GetServerConfigurableFlag(
292 "aconfig_test",
293 "com.android.aconfig.test.disabled_rw",
294 "false") == "true";
295 }
296
297 virtual bool enabled_ro() override {
298 return true;
299 }
300
301 virtual bool enabled_rw() override {
302 return server_configurable_flags::GetServerConfigurableFlag(
303 "aconfig_test",
304 "com.android.aconfig.test.enabled_rw",
305 "true") == "true";
306 }
307
308 };
309
310 std::unique_ptr<flag_provider_interface> provider_ =
311 std::make_unique<flag_provider>();
312}
Dennis Shen7321f4f2023-07-11 15:45:00 +0000313
314bool com_android_aconfig_test_disabled_ro() {
Dennis Shen5c242132023-07-14 14:57:08 +0000315 return false;
Dennis Shen7321f4f2023-07-11 15:45:00 +0000316}
317
318bool com_android_aconfig_test_disabled_rw() {
319 return com::android::aconfig::test::disabled_rw();
320}
321
322bool com_android_aconfig_test_enabled_ro() {
Dennis Shen5c242132023-07-14 14:57:08 +0000323 return true;
Dennis Shen7321f4f2023-07-11 15:45:00 +0000324}
325
326bool com_android_aconfig_test_enabled_rw() {
327 return com::android::aconfig::test::enabled_rw();
328}
Joe Onoratoac692c52023-07-18 17:29:14 -0700329
Dennis Shen17a08eec2023-07-14 01:32:50 +0000330"#;
Dennis Shen7321f4f2023-07-11 15:45:00 +0000331
Joe Onoratoac692c52023-07-18 17:29:14 -0700332 const TEST_SOURCE_FILE_EXPECTED: &str = r#"
Dennis Shen17a08eec2023-07-14 01:32:50 +0000333#include "com_android_aconfig_test.h"
Joe Onoratoac692c52023-07-18 17:29:14 -0700334#include <server_configurable_flags/get_flags.h>
335
336namespace com::android::aconfig::test {
337
338 class flag_provider : public flag_provider_interface {
339 private:
340 std::unordered_map<std::string, bool> overrides_;
341
342 public:
343 flag_provider()
344 : overrides_()
345 {}
346
347 virtual bool disabled_ro() override {
348 auto it = overrides_.find("disabled_ro");
349 if (it != overrides_.end()) {
350 return it->second;
351 } else {
352 return false;
353 }
354 }
355
356 virtual void disabled_ro(bool val) override {
357 overrides_["disabled_ro"] = val;
358 }
359
360 virtual bool disabled_rw() override {
361 auto it = overrides_.find("disabled_rw");
362 if (it != overrides_.end()) {
363 return it->second;
364 } else {
365 return server_configurable_flags::GetServerConfigurableFlag(
366 "aconfig_test",
367 "com.android.aconfig.test.disabled_rw",
368 "false") == "true";
369 }
370 }
371
372 virtual void disabled_rw(bool val) override {
373 overrides_["disabled_rw"] = val;
374 }
375
376 virtual bool enabled_ro() override {
377 auto it = overrides_.find("enabled_ro");
378 if (it != overrides_.end()) {
379 return it->second;
380 } else {
381 return true;
382 }
383 }
384
385 virtual void enabled_ro(bool val) override {
386 overrides_["enabled_ro"] = val;
387 }
388
389 virtual bool enabled_rw() override {
390 auto it = overrides_.find("enabled_rw");
391 if (it != overrides_.end()) {
392 return it->second;
393 } else {
394 return server_configurable_flags::GetServerConfigurableFlag(
395 "aconfig_test",
396 "com.android.aconfig.test.enabled_rw",
397 "true") == "true";
398 }
399 }
400
401 virtual void enabled_rw(bool val) override {
402 overrides_["enabled_rw"] = val;
403 }
404
405
406 virtual void reset_flags() override {
407 overrides_.clear();
408 }
409 };
410
411 std::unique_ptr<flag_provider_interface> provider_ =
412 std::make_unique<flag_provider>();
413}
Dennis Shen17a08eec2023-07-14 01:32:50 +0000414
415bool com_android_aconfig_test_disabled_ro() {
416 return com::android::aconfig::test::disabled_ro();
Dennis Shen7321f4f2023-07-11 15:45:00 +0000417}
418
Joe Onoratoac692c52023-07-18 17:29:14 -0700419
Dennis Shen17a08eec2023-07-14 01:32:50 +0000420void set_com_android_aconfig_test_disabled_ro(bool val) {
421 com::android::aconfig::test::disabled_ro(val);
422}
423
424bool com_android_aconfig_test_disabled_rw() {
425 return com::android::aconfig::test::disabled_rw();
426}
427
Joe Onoratoac692c52023-07-18 17:29:14 -0700428
Dennis Shen17a08eec2023-07-14 01:32:50 +0000429void set_com_android_aconfig_test_disabled_rw(bool val) {
430 com::android::aconfig::test::disabled_rw(val);
431}
432
433bool com_android_aconfig_test_enabled_ro() {
434 return com::android::aconfig::test::enabled_ro();
435}
436
Joe Onoratoac692c52023-07-18 17:29:14 -0700437
Dennis Shen17a08eec2023-07-14 01:32:50 +0000438void set_com_android_aconfig_test_enabled_ro(bool val) {
439 com::android::aconfig::test::enabled_ro(val);
440}
441
442bool com_android_aconfig_test_enabled_rw() {
443 return com::android::aconfig::test::enabled_rw();
444}
445
Joe Onoratoac692c52023-07-18 17:29:14 -0700446
Dennis Shen17a08eec2023-07-14 01:32:50 +0000447void set_com_android_aconfig_test_enabled_rw(bool val) {
448 com::android::aconfig::test::enabled_rw(val);
449}
450
451void com_android_aconfig_test_reset_flags() {
Joe Onoratoac692c52023-07-18 17:29:14 -0700452 com::android::aconfig::test::reset_flags();
Dennis Shen7321f4f2023-07-11 15:45:00 +0000453}
Joe Onoratoac692c52023-07-18 17:29:14 -0700454
Dennis Shen7321f4f2023-07-11 15:45:00 +0000455"#;
Joe Onoratoac692c52023-07-18 17:29:14 -0700456
Dennis Shen8d544f72023-06-29 00:45:42 +0000457 fn test_generate_cpp_code(mode: CodegenMode) {
458 let parsed_flags = crate::test::parse_test_flags();
459 let generated =
460 generate_cpp_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter(), mode)
461 .unwrap();
462 let mut generated_files_map = HashMap::new();
463 for file in generated {
464 generated_files_map.insert(
465 String::from(file.path.to_str().unwrap()),
466 String::from_utf8(file.contents.clone()).unwrap(),
467 );
468 }
469
470 let mut target_file_path = String::from("include/com_android_aconfig_test.h");
471 assert!(generated_files_map.contains_key(&target_file_path));
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000472 assert_eq!(
Mårten Kongstadb0255072023-06-08 10:15:43 +0200473 None,
474 crate::test::first_significant_code_diff(
Dennis Shen8d544f72023-06-29 00:45:42 +0000475 match mode {
476 CodegenMode::Production => EXPORTED_PROD_HEADER_EXPECTED,
477 CodegenMode::Test => EXPORTED_TEST_HEADER_EXPECTED,
478 },
479 generated_files_map.get(&target_file_path).unwrap()
Mårten Kongstadb0255072023-06-08 10:15:43 +0200480 )
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000481 );
Dennis Shen8d544f72023-06-29 00:45:42 +0000482
Dennis Shen8d544f72023-06-29 00:45:42 +0000483 target_file_path = String::from("com_android_aconfig_test.cc");
484 assert!(generated_files_map.contains_key(&target_file_path));
485 assert_eq!(
486 None,
487 crate::test::first_significant_code_diff(
Dennis Shen17a08eec2023-07-14 01:32:50 +0000488 match mode {
Joe Onoratoac692c52023-07-18 17:29:14 -0700489 CodegenMode::Production => PROD_SOURCE_FILE_EXPECTED,
490 CodegenMode::Test => TEST_SOURCE_FILE_EXPECTED,
Dennis Shen17a08eec2023-07-14 01:32:50 +0000491 },
Dennis Shen7321f4f2023-07-11 15:45:00 +0000492 generated_files_map.get(&target_file_path).unwrap()
493 )
494 );
Dennis Shen8d544f72023-06-29 00:45:42 +0000495 }
496
497 #[test]
498 fn test_generate_cpp_code_for_prod() {
499 test_generate_cpp_code(CodegenMode::Production);
500 }
501
502 #[test]
503 fn test_generate_cpp_code_for_test() {
504 test_generate_cpp_code(CodegenMode::Test);
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000505 }
506}