Merge "aconfig: Add codegen for java" am: 7b6aacb055
am: 86d0c527c0
am: ed130f11ed
Original change: https://android-review.googlesource.com/c/platform/build/+/2583478 Change-Id: I270ab7ba6ec672bc744a5b535501cf1f050f8fb0 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
6e6c5ca7b7
6 changed files with 185 additions and 0 deletions
|
@ -23,6 +23,7 @@ rust_defaults {
|
|||
"libprotobuf",
|
||||
"libserde",
|
||||
"libserde_json",
|
||||
"libtinytemplate",
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ clap = { version = "4.1.8", features = ["derive"] }
|
|||
protobuf = "3.2.0"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
tinytemplate = "1.2.1"
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
|
|
142
tools/aconfig/src/codegen_java.rs
Normal file
142
tools/aconfig/src/codegen_java.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
use tinytemplate::TinyTemplate;
|
||||
|
||||
use crate::aconfig::{FlagState, Permission};
|
||||
use crate::cache::{Cache, Item};
|
||||
|
||||
pub struct GeneratedFile {
|
||||
pub file_content: String,
|
||||
pub file_name: String,
|
||||
}
|
||||
|
||||
pub fn generate_java_code(cache: &Cache) -> Result<GeneratedFile> {
|
||||
let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
|
||||
let readwrite = class_elements.iter().any(|item| item.readwrite);
|
||||
let namespace = uppercase_first_letter(
|
||||
cache.iter().find(|item| !item.namespace.is_empty()).unwrap().namespace.as_str(),
|
||||
);
|
||||
let context = Context { namespace: namespace.clone(), readwrite, class_elements };
|
||||
let mut template = TinyTemplate::new();
|
||||
template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
|
||||
let file_content = template.render("java_code_gen", &context)?;
|
||||
Ok(GeneratedFile { file_content, file_name: format!("{}.java", namespace) })
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Context {
|
||||
pub namespace: String,
|
||||
pub readwrite: bool,
|
||||
pub class_elements: Vec<ClassElement>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ClassElement {
|
||||
pub method_name: String,
|
||||
pub readwrite: bool,
|
||||
pub default_value: String,
|
||||
pub feature_name: String,
|
||||
pub flag_name: String,
|
||||
}
|
||||
|
||||
fn create_class_element(item: &Item) -> ClassElement {
|
||||
ClassElement {
|
||||
method_name: item.name.clone(),
|
||||
readwrite: item.permission == Permission::ReadWrite,
|
||||
default_value: if item.state == FlagState::Enabled {
|
||||
"true".to_string()
|
||||
} else {
|
||||
"false".to_string()
|
||||
},
|
||||
feature_name: item.name.clone(),
|
||||
flag_name: item.name.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn uppercase_first_letter(s: &str) -> String {
|
||||
s.chars()
|
||||
.enumerate()
|
||||
.map(
|
||||
|(index, ch)| {
|
||||
if index == 0 {
|
||||
ch.to_ascii_uppercase()
|
||||
} else {
|
||||
ch.to_ascii_lowercase()
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::aconfig::{Flag, Value};
|
||||
use crate::commands::Source;
|
||||
|
||||
#[test]
|
||||
fn test_generate_java_code() {
|
||||
let namespace = "TeSTFlaG";
|
||||
let mut cache = Cache::new(1, namespace.to_string());
|
||||
cache
|
||||
.add_flag(
|
||||
Source::File("test.txt".to_string()),
|
||||
Flag {
|
||||
name: "test".to_string(),
|
||||
description: "buildtime enable".to_string(),
|
||||
values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
cache
|
||||
.add_flag(
|
||||
Source::File("test2.txt".to_string()),
|
||||
Flag {
|
||||
name: "test2".to_string(),
|
||||
description: "runtime disable".to_string(),
|
||||
values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let expect_content = "package com.android.aconfig;
|
||||
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
public final class Testflag {
|
||||
|
||||
public static boolean test() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean test2() {
|
||||
return DeviceConfig.getBoolean(
|
||||
\"Testflag\",
|
||||
\"test2__test2\",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
";
|
||||
let expected_file_name = format!("{}.java", uppercase_first_letter(namespace));
|
||||
let generated_file = generate_java_code(&cache).unwrap();
|
||||
assert_eq!(expected_file_name, generated_file.file_name);
|
||||
assert_eq!(expect_content.replace(' ', ""), generated_file.file_content.replace(' ', ""));
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ use std::io::Read;
|
|||
|
||||
use crate::aconfig::{Namespace, Override};
|
||||
use crate::cache::Cache;
|
||||
use crate::codegen_java::{generate_java_code, GeneratedFile};
|
||||
use crate::protos::ProtoParsedFlags;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
|
@ -84,6 +85,10 @@ pub fn create_cache(
|
|||
Ok(cache)
|
||||
}
|
||||
|
||||
pub fn generate_code(cache: &Cache) -> Result<GeneratedFile> {
|
||||
generate_java_code(cache)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
|
||||
pub enum Format {
|
||||
Text,
|
||||
|
|
|
@ -24,6 +24,7 @@ use std::io::Write;
|
|||
|
||||
mod aconfig;
|
||||
mod cache;
|
||||
mod codegen_java;
|
||||
mod commands;
|
||||
mod protos;
|
||||
|
||||
|
@ -46,6 +47,11 @@ fn cli() -> Command {
|
|||
.arg(Arg::new("override").long("override").action(ArgAction::Append))
|
||||
.arg(Arg::new("cache").long("cache").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("create-java-lib")
|
||||
.arg(Arg::new("cache").long("cache").required(true))
|
||||
.arg(Arg::new("out").long("out").required(true)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("dump")
|
||||
.arg(Arg::new("cache").long("cache").required(true))
|
||||
|
@ -81,6 +87,17 @@ fn main() -> Result<()> {
|
|||
let file = fs::File::create(path)?;
|
||||
cache.write_to_writer(file)?;
|
||||
}
|
||||
Some(("create-java-lib", sub_matches)) => {
|
||||
let path = sub_matches.get_one::<String>("cache").unwrap();
|
||||
let file = fs::File::open(path)?;
|
||||
let cache = Cache::read_from_reader(file)?;
|
||||
let out = sub_matches.get_one::<String>("out").unwrap();
|
||||
let generated_file = commands::generate_code(&cache).unwrap();
|
||||
fs::write(
|
||||
format!("{}/{}", out, generated_file.file_name),
|
||||
generated_file.file_content,
|
||||
)?;
|
||||
}
|
||||
Some(("dump", sub_matches)) => {
|
||||
let path = sub_matches.get_one::<String>("cache").unwrap();
|
||||
let file = fs::File::open(path)?;
|
||||
|
|
19
tools/aconfig/templates/java.template
Normal file
19
tools/aconfig/templates/java.template
Normal file
|
@ -0,0 +1,19 @@
|
|||
package com.android.aconfig;
|
||||
{{ if readwrite }}
|
||||
import android.provider.DeviceConfig;
|
||||
{{ endif }}
|
||||
public final class {namespace} \{
|
||||
{{ for item in class_elements}}
|
||||
public static boolean {item.method_name}() \{
|
||||
{{ if item.readwrite- }}
|
||||
return DeviceConfig.getBoolean(
|
||||
"{namespace}",
|
||||
"{item.feature_name}__{item.flag_name}",
|
||||
{item.default_value}
|
||||
);
|
||||
{{ -else- }}
|
||||
return {item.default_value};
|
||||
{{ -endif }}
|
||||
}
|
||||
{{ endfor }}
|
||||
}
|
Loading…
Reference in a new issue