blob: 979b8ad893c72b939186fabece515890b00624a0 [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 Shen8d544f72023-06-29 00:45:42 +000045 for_prod: codegen_mode == CodegenMode::Production,
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 },
60 FileSpec {
61 name: &format!("{}_flag_provider.h", header),
62 template: match codegen_mode {
63 CodegenMode::Production => {
64 include_str!("../templates/cpp_prod_flag_provider.template")
65 }
66 CodegenMode::Test => include_str!("../templates/cpp_test_flag_provider.template"),
67 },
68 dir: "",
69 },
Dennis Shen7321f4f2023-07-11 15:45:00 +000070 FileSpec {
71 name: &format!("{}_c.h", header),
72 template: include_str!("../templates/c_exported_header.template"),
73 dir: "include",
74 },
75 FileSpec {
76 name: &format!("{}_c.cc", header),
77 template: include_str!("../templates/c_source_file.template"),
78 dir: "",
79 },
Dennis Shen8d544f72023-06-29 00:45:42 +000080 ];
81 files.iter().map(|file| generate_file(file, &context)).collect()
82}
83
84pub fn generate_file(file: &FileSpec, context: &Context) -> Result<OutputFile> {
Dennis Shen1dc9ad42023-05-12 00:21:55 +000085 let mut template = TinyTemplate::new();
Dennis Shen8d544f72023-06-29 00:45:42 +000086 template.add_template(file.name, file.template)?;
87 let contents = template.render(file.name, &context)?;
88 let path: PathBuf = [&file.dir, &file.name].iter().collect();
Dennis Shen1dc9ad42023-05-12 00:21:55 +000089 Ok(OutputFile { contents: contents.into(), path })
90}
91
92#[derive(Serialize)]
Dennis Shen8d544f72023-06-29 00:45:42 +000093pub struct FileSpec<'a> {
94 pub name: &'a str,
95 pub template: &'a str,
96 pub dir: &'a str,
97}
98
99#[derive(Serialize)]
100pub struct Context {
Mårten Kongstadfbd71e22023-05-31 13:29:35 +0200101 pub header: String,
102 pub cpp_namespace: String,
Mårten Kongstad9fb58962023-05-31 13:02:13 +0200103 pub package: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000104 pub readwrite: bool,
Dennis Shen8d544f72023-06-29 00:45:42 +0000105 pub for_prod: bool,
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000106 pub class_elements: Vec<ClassElement>,
107}
108
109#[derive(Serialize)]
Dennis Shen8d544f72023-06-29 00:45:42 +0000110pub struct ClassElement {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000111 pub readwrite: bool,
112 pub default_value: String,
113 pub flag_name: String,
Dennis Shen8d544f72023-06-29 00:45:42 +0000114 pub uppercase_flag_name: String,
Mårten Kongstad066575b2023-06-07 16:29:25 +0200115 pub device_config_namespace: String,
116 pub device_config_flag: String,
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000117}
118
Mårten Kongstad403658f2023-06-14 09:51:56 +0200119fn create_class_element(package: &str, pf: &ProtoParsedFlag) -> ClassElement {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000120 ClassElement {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200121 readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,
122 default_value: if pf.state() == ProtoFlagState::ENABLED {
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000123 "true".to_string()
124 } else {
125 "false".to_string()
126 },
Mårten Kongstad403658f2023-06-14 09:51:56 +0200127 flag_name: pf.name().to_string(),
Dennis Shen8d544f72023-06-29 00:45:42 +0000128 uppercase_flag_name: pf.name().to_string().to_ascii_uppercase(),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200129 device_config_namespace: pf.namespace().to_string(),
130 device_config_flag: codegen::create_device_config_ident(package, pf.name())
131 .expect("values checked at flag parse time"),
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
Dennis Shen8d544f72023-06-29 00:45:42 +0000138 use std::collections::HashMap;
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000139
Dennis Shen8d544f72023-06-29 00:45:42 +0000140 const EXPORTED_PROD_HEADER_EXPECTED: &str = r#"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200141#ifndef com_android_aconfig_test_HEADER_H
142#define com_android_aconfig_test_HEADER_H
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000143
Dennis Shen8d544f72023-06-29 00:45:42 +0000144#include <string>
145#include <memory>
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000146
Mårten Kongstad403658f2023-06-14 09:51:56 +0200147namespace com::android::aconfig::test {
Dennis Shen8d544f72023-06-29 00:45:42 +0000148class flag_provider_interface {
149public:
150
151 virtual ~flag_provider_interface() = default;
152
153 virtual bool disabled_ro() = 0;
154
155 virtual bool disabled_rw() = 0;
156
157 virtual bool enabled_ro() = 0;
158
159 virtual bool enabled_rw() = 0;
160
161 virtual void override_flag(std::string const&, bool) {}
162
163 virtual void reset_overrides() {}
164};
165
166extern std::unique_ptr<flag_provider_interface> provider_;
167
168extern std::string const DISABLED_RO;
169extern std::string const DISABLED_RW;
170extern std::string const ENABLED_RO;
171extern std::string const ENABLED_RW;
172
173inline bool disabled_ro() {
174 return false;
175}
176
177inline bool disabled_rw() {
178 return provider_->disabled_rw();
179}
180
181inline bool enabled_ro() {
182 return true;
183}
184
185inline bool enabled_rw() {
186 return provider_->enabled_rw();
187}
188
189inline void override_flag(std::string const& name, bool val) {
190 return provider_->override_flag(name, val);
191}
192
193inline void reset_overrides() {
194 return provider_->reset_overrides();
195}
196
197}
198#endif
199"#;
200
201 const EXPORTED_TEST_HEADER_EXPECTED: &str = r#"
202#ifndef com_android_aconfig_test_HEADER_H
203#define com_android_aconfig_test_HEADER_H
204
205#include <string>
206#include <memory>
Dennis Shen8d544f72023-06-29 00:45:42 +0000207
208namespace com::android::aconfig::test {
209class flag_provider_interface {
210public:
211
212 virtual ~flag_provider_interface() = default;
213
214 virtual bool disabled_ro() = 0;
215
216 virtual bool disabled_rw() = 0;
217
218 virtual bool enabled_ro() = 0;
219
220 virtual bool enabled_rw() = 0;
221
222 virtual void override_flag(std::string const&, bool) {}
223
224 virtual void reset_overrides() {}
225};
226
227extern std::unique_ptr<flag_provider_interface> provider_;
228
229extern std::string const DISABLED_RO;
230extern std::string const DISABLED_RW;
231extern std::string const ENABLED_RO;
232extern std::string const ENABLED_RW;
233
234inline bool disabled_ro() {
235 return provider_->disabled_ro();
236}
237
238inline bool disabled_rw() {
239 return provider_->disabled_rw();
240}
241
242inline bool enabled_ro() {
243 return provider_->enabled_ro();
244}
245
246inline bool enabled_rw() {
247 return provider_->enabled_rw();
248}
249
250inline void override_flag(std::string const& name, bool val) {
251 return provider_->override_flag(name, val);
252}
253
254inline void reset_overrides() {
255 return provider_->reset_overrides();
256}
257
258}
259#endif
260"#;
261
262 const PROD_FLAG_PROVIDER_HEADER_EXPECTED: &str = r#"
263#ifndef com_android_aconfig_test_flag_provider_HEADER_H
264#define com_android_aconfig_test_flag_provider_HEADER_H
265
266#include "com_android_aconfig_test.h"
Dennis Shene3981442023-07-10 18:15:42 +0000267#include <server_configurable_flags/get_flags.h>
268using namespace server_configurable_flags;
Dennis Shen8d544f72023-06-29 00:45:42 +0000269
270namespace com::android::aconfig::test {
271class flag_provider : public flag_provider_interface {
272public:
273
274 virtual bool disabled_ro() override {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200275 return false;
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000276 }
277
Dennis Shen8d544f72023-06-29 00:45:42 +0000278 virtual bool disabled_rw() override {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200279 return GetServerConfigurableFlag(
280 "aconfig_test",
281 "com.android.aconfig.test.disabled_rw",
282 "false") == "true";
283 }
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000284
Dennis Shen8d544f72023-06-29 00:45:42 +0000285 virtual bool enabled_ro() override {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200286 return true;
287 }
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000288
Dennis Shen8d544f72023-06-29 00:45:42 +0000289 virtual bool enabled_rw() override {
Mårten Kongstad403658f2023-06-14 09:51:56 +0200290 return GetServerConfigurableFlag(
291 "aconfig_test",
292 "com.android.aconfig.test.enabled_rw",
293 "true") == "true";
294 }
Dennis Shen8d544f72023-06-29 00:45:42 +0000295};
Mårten Kongstad403658f2023-06-14 09:51:56 +0200296}
297#endif
298"#;
Dennis Shen8d544f72023-06-29 00:45:42 +0000299
300 const TEST_FLAG_PROVIDER_HEADER_EXPECTED: &str = r#"
301#ifndef com_android_aconfig_test_flag_provider_HEADER_H
302#define com_android_aconfig_test_flag_provider_HEADER_H
303
304#include "com_android_aconfig_test.h"
Dennis Shene3981442023-07-10 18:15:42 +0000305#include <server_configurable_flags/get_flags.h>
306using namespace server_configurable_flags;
Dennis Shen8d544f72023-06-29 00:45:42 +0000307
308#include <unordered_map>
309#include <unordered_set>
310#include <cassert>
311
312namespace com::android::aconfig::test {
313class flag_provider : public flag_provider_interface {
314private:
315 std::unordered_map<std::string, bool> overrides_;
316 std::unordered_set<std::string> flag_names_;
317
318public:
319
320 flag_provider()
321 : overrides_(),
322 flag_names_() {
323 flag_names_.insert(DISABLED_RO);
324 flag_names_.insert(DISABLED_RW);
325 flag_names_.insert(ENABLED_RO);
326 flag_names_.insert(ENABLED_RW);
327 }
328
329 virtual bool disabled_ro() override {
330 auto it = overrides_.find(DISABLED_RO);
331 if (it != overrides_.end()) {
332 return it->second;
333 } else {
334 return false;
335 }
336 }
337
338 virtual bool disabled_rw() override {
339 auto it = overrides_.find(DISABLED_RW);
340 if (it != overrides_.end()) {
341 return it->second;
342 } else {
343 return GetServerConfigurableFlag(
344 "aconfig_test",
345 "com.android.aconfig.test.disabled_rw",
346 "false") == "true";
347 }
348 }
349
350 virtual bool enabled_ro() override {
351 auto it = overrides_.find(ENABLED_RO);
352 if (it != overrides_.end()) {
353 return it->second;
354 } else {
355 return true;
356 }
357 }
358
359 virtual bool enabled_rw() override {
360 auto it = overrides_.find(ENABLED_RW);
361 if (it != overrides_.end()) {
362 return it->second;
363 } else {
364 return GetServerConfigurableFlag(
365 "aconfig_test",
366 "com.android.aconfig.test.enabled_rw",
367 "true") == "true";
368 }
369 }
370
371 virtual void override_flag(std::string const& flag, bool val) override {
372 assert(flag_names_.count(flag));
373 overrides_[flag] = val;
374 }
375
376 virtual void reset_overrides() override {
377 overrides_.clear();
378 }
379};
380}
381#endif
382"#;
383
384 const SOURCE_FILE_EXPECTED: &str = r#"
385#include "com_android_aconfig_test.h"
386#include "com_android_aconfig_test_flag_provider.h"
387
388namespace com::android::aconfig::test {
389
390 std::string const DISABLED_RO = "com.android.aconfig.test.disabled_ro";
391 std::string const DISABLED_RW = "com.android.aconfig.test.disabled_rw";
392 std::string const ENABLED_RO = "com.android.aconfig.test.enabled_ro";
393 std::string const ENABLED_RW = "com.android.aconfig.test.enabled_rw";
394
395 std::unique_ptr<flag_provider_interface> provider_ =
396 std::make_unique<flag_provider>();
397}
398"#;
399
Dennis Shen7321f4f2023-07-11 15:45:00 +0000400 const C_EXPORTED_HEADER_EXPECTED: &str = r#"
401#ifndef com_android_aconfig_test_c_HEADER_H
402#define com_android_aconfig_test_c_HEADER_H
403
404#ifdef __cplusplus
405extern "C" {
406#endif
407
408extern const char* com_android_aconfig_test_DISABLED_RO;
409extern const char* com_android_aconfig_test_DISABLED_RW;
410extern const char* com_android_aconfig_test_ENABLED_RO;
411extern const char* com_android_aconfig_test_ENABLED_RW;
412
413bool com_android_aconfig_test_disabled_ro();
414
415bool com_android_aconfig_test_disabled_rw();
416
417bool com_android_aconfig_test_enabled_ro();
418
419bool com_android_aconfig_test_enabled_rw();
420
421void com_android_aconfig_test_override_flag(const char* name, bool val);
422
423void com_android_aconfig_test_reset_overrides();
424
425#ifdef __cplusplus
426}
427#endif
428#endif
429"#;
430
431 const C_SOURCE_FILE_EXPECTED: &str = r#"
432#include "com_android_aconfig_test_c.h"
433#include "com_android_aconfig_test.h"
434#include <string>
435
436const char* com_android_aconfig_test_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
437const char* com_android_aconfig_test_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
438const char* com_android_aconfig_test_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
439const char* com_android_aconfig_test_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
440
441bool com_android_aconfig_test_disabled_ro() {
442 return com::android::aconfig::test::disabled_ro();
443}
444
445bool com_android_aconfig_test_disabled_rw() {
446 return com::android::aconfig::test::disabled_rw();
447}
448
449bool com_android_aconfig_test_enabled_ro() {
450 return com::android::aconfig::test::enabled_ro();
451}
452
453bool com_android_aconfig_test_enabled_rw() {
454 return com::android::aconfig::test::enabled_rw();
455}
456
457void com_android_aconfig_test_override_flag(const char* name, bool val) {
458 com::android::aconfig::test::override_flag(std::string(name), val);
459}
460
461void com_android_aconfig_test_reset_overrides() {
462 com::android::aconfig::test::reset_overrides();
463}
464"#;
Dennis Shen8d544f72023-06-29 00:45:42 +0000465 fn test_generate_cpp_code(mode: CodegenMode) {
466 let parsed_flags = crate::test::parse_test_flags();
467 let generated =
468 generate_cpp_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter(), mode)
469 .unwrap();
470 let mut generated_files_map = HashMap::new();
471 for file in generated {
472 generated_files_map.insert(
473 String::from(file.path.to_str().unwrap()),
474 String::from_utf8(file.contents.clone()).unwrap(),
475 );
476 }
477
478 let mut target_file_path = String::from("include/com_android_aconfig_test.h");
479 assert!(generated_files_map.contains_key(&target_file_path));
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000480 assert_eq!(
Mårten Kongstadb0255072023-06-08 10:15:43 +0200481 None,
482 crate::test::first_significant_code_diff(
Dennis Shen8d544f72023-06-29 00:45:42 +0000483 match mode {
484 CodegenMode::Production => EXPORTED_PROD_HEADER_EXPECTED,
485 CodegenMode::Test => EXPORTED_TEST_HEADER_EXPECTED,
486 },
487 generated_files_map.get(&target_file_path).unwrap()
Mårten Kongstadb0255072023-06-08 10:15:43 +0200488 )
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000489 );
Dennis Shen8d544f72023-06-29 00:45:42 +0000490
491 target_file_path = String::from("com_android_aconfig_test_flag_provider.h");
492 assert!(generated_files_map.contains_key(&target_file_path));
493 assert_eq!(
494 None,
495 crate::test::first_significant_code_diff(
496 match mode {
497 CodegenMode::Production => PROD_FLAG_PROVIDER_HEADER_EXPECTED,
498 CodegenMode::Test => TEST_FLAG_PROVIDER_HEADER_EXPECTED,
499 },
500 generated_files_map.get(&target_file_path).unwrap()
501 )
502 );
503
504 target_file_path = String::from("com_android_aconfig_test.cc");
505 assert!(generated_files_map.contains_key(&target_file_path));
506 assert_eq!(
507 None,
508 crate::test::first_significant_code_diff(
509 SOURCE_FILE_EXPECTED,
510 generated_files_map.get(&target_file_path).unwrap()
511 )
512 );
Dennis Shen7321f4f2023-07-11 15:45:00 +0000513
514 target_file_path = String::from("include/com_android_aconfig_test_c.h");
515 assert!(generated_files_map.contains_key(&target_file_path));
516 assert_eq!(
517 None,
518 crate::test::first_significant_code_diff(
519 C_EXPORTED_HEADER_EXPECTED,
520 generated_files_map.get(&target_file_path).unwrap()
521 )
522 );
523
524 target_file_path = String::from("com_android_aconfig_test_c.cc");
525 assert!(generated_files_map.contains_key(&target_file_path));
526 assert_eq!(
527 None,
528 crate::test::first_significant_code_diff(
529 C_SOURCE_FILE_EXPECTED,
530 generated_files_map.get(&target_file_path).unwrap()
531 )
532 );
Dennis Shen8d544f72023-06-29 00:45:42 +0000533 }
534
535 #[test]
536 fn test_generate_cpp_code_for_prod() {
537 test_generate_cpp_code(CodegenMode::Production);
538 }
539
540 #[test]
541 fn test_generate_cpp_code_for_test() {
542 test_generate_cpp_code(CodegenMode::Test);
Dennis Shen1dc9ad42023-05-12 00:21:55 +0000543 }
544}