Merge "aconfig: Java codegen iteration 1" am: 06377d79ab
Original change: https://android-review.googlesource.com/c/platform/build/+/2619534 Change-Id: I3ef56bcda7625dcaf70abb937b1b6503b4e7333d Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -24,43 +24,59 @@ use crate::cache::{Cache, Item};
|
||||
use crate::codegen;
|
||||
use crate::commands::OutputFile;
|
||||
|
||||
pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
|
||||
pub fn generate_java_code(cache: &Cache) -> Result<Vec<OutputFile>> {
|
||||
let package = cache.package();
|
||||
let class_elements: Vec<ClassElement> =
|
||||
cache.iter().map(|item| create_class_element(package, item)).collect();
|
||||
let readwrite = class_elements.iter().any(|item| item.readwrite);
|
||||
let context = Context { package: package.to_string(), readwrite, class_elements };
|
||||
let is_read_write = class_elements.iter().any(|item| item.is_read_write);
|
||||
let context = Context { package_name: package.to_string(), is_read_write, class_elements };
|
||||
|
||||
let java_files = vec!["Flags.java", "FeatureFlagsImpl.java", "FeatureFlags.java"];
|
||||
|
||||
let mut template = TinyTemplate::new();
|
||||
template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
|
||||
let contents = template.render("java_code_gen", &context)?;
|
||||
let mut path: PathBuf = package.split('.').collect();
|
||||
// TODO: Allow customization of the java class name
|
||||
path.push("Flags.java");
|
||||
Ok(OutputFile { contents: contents.into(), path })
|
||||
template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
|
||||
template.add_template(
|
||||
"FeatureFlagsImpl.java",
|
||||
include_str!("../templates/FeatureFlagsImpl.java.template"),
|
||||
)?;
|
||||
template.add_template(
|
||||
"FeatureFlags.java",
|
||||
include_str!("../templates/FeatureFlags.java.template"),
|
||||
)?;
|
||||
|
||||
let path: PathBuf = package.split('.').collect();
|
||||
java_files
|
||||
.iter()
|
||||
.map(|file| {
|
||||
Ok(OutputFile {
|
||||
contents: template.render(file, &context)?.into(),
|
||||
path: path.join(file),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<OutputFile>>>()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Context {
|
||||
pub package: String,
|
||||
pub readwrite: bool,
|
||||
pub package_name: String,
|
||||
pub is_read_write: bool,
|
||||
pub class_elements: Vec<ClassElement>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ClassElement {
|
||||
pub method_name: String,
|
||||
pub readwrite: bool,
|
||||
pub default_value: String,
|
||||
pub device_config_namespace: String,
|
||||
pub device_config_flag: String,
|
||||
pub flag_name_constant_suffix: String,
|
||||
pub is_read_write: bool,
|
||||
pub method_name: String,
|
||||
}
|
||||
|
||||
fn create_class_element(package: &str, item: &Item) -> ClassElement {
|
||||
let device_config_flag = codegen::create_device_config_ident(package, &item.name)
|
||||
.expect("values checked at cache creation time");
|
||||
ClassElement {
|
||||
method_name: item.name.replace('-', "_"),
|
||||
readwrite: item.permission == Permission::ReadWrite,
|
||||
default_value: if item.state == FlagState::Enabled {
|
||||
"true".to_string()
|
||||
} else {
|
||||
@@ -68,78 +84,100 @@ fn create_class_element(package: &str, item: &Item) -> ClassElement {
|
||||
},
|
||||
device_config_namespace: item.namespace.clone(),
|
||||
device_config_flag,
|
||||
flag_name_constant_suffix: item.name.to_ascii_uppercase(),
|
||||
is_read_write: item.permission == Permission::ReadWrite,
|
||||
method_name: item.name.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::aconfig::{FlagDeclaration, FlagValue};
|
||||
use crate::cache::CacheBuilder;
|
||||
use crate::commands::Source;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test_generate_java_code() {
|
||||
let package = "com.example";
|
||||
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
||||
builder
|
||||
.add_flag_declaration(
|
||||
Source::File("test.txt".to_string()),
|
||||
FlagDeclaration {
|
||||
name: "test".to_string(),
|
||||
namespace: "ns".to_string(),
|
||||
description: "buildtime enable".to_string(),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.add_flag_declaration(
|
||||
Source::File("test2.txt".to_string()),
|
||||
FlagDeclaration {
|
||||
name: "test2".to_string(),
|
||||
namespace: "ns".to_string(),
|
||||
description: "runtime disable".to_string(),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.add_flag_value(
|
||||
Source::Memory,
|
||||
FlagValue {
|
||||
package: package.to_string(),
|
||||
name: "test".to_string(),
|
||||
state: FlagState::Disabled,
|
||||
permission: Permission::ReadOnly,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let cache = builder.build();
|
||||
let expect_content = r#"package com.example;
|
||||
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
let cache = crate::test::create_cache();
|
||||
let generated_files = generate_java_code(&cache).unwrap();
|
||||
let expect_flags_content = r#"
|
||||
package com.android.aconfig.test;
|
||||
public final class Flags {
|
||||
|
||||
public static boolean test() {
|
||||
return false;
|
||||
public static boolean disabled_ro() {
|
||||
return FEATURE_FLAGS.disabled_ro();
|
||||
}
|
||||
|
||||
public static boolean test2() {
|
||||
return DeviceConfig.getBoolean(
|
||||
"ns",
|
||||
"com.example.test2",
|
||||
false
|
||||
);
|
||||
public static boolean disabled_rw() {
|
||||
return FEATURE_FLAGS.disabled_rw();
|
||||
}
|
||||
public static boolean enabled_ro() {
|
||||
return FEATURE_FLAGS.enabled_ro();
|
||||
}
|
||||
public static boolean enabled_rw() {
|
||||
return FEATURE_FLAGS.enabled_rw();
|
||||
}
|
||||
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
||||
|
||||
}
|
||||
"#;
|
||||
let file = generate_java_code(&cache).unwrap();
|
||||
assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
|
||||
assert_eq!(
|
||||
None,
|
||||
crate::test::first_significant_code_diff(
|
||||
expect_content,
|
||||
&String::from_utf8(file.contents).unwrap()
|
||||
)
|
||||
);
|
||||
let expected_featureflagsimpl_content = r#"
|
||||
package com.android.aconfig.test;
|
||||
import android.provider.DeviceConfig;
|
||||
public final class FeatureFlagsImpl implements FeatureFlags {
|
||||
@Override
|
||||
public boolean disabled_ro() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean disabled_rw() {
|
||||
return DeviceConfig.getBoolean(
|
||||
"aconfig_test",
|
||||
"com.android.aconfig.test.disabled_rw",
|
||||
false
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public boolean enabled_ro() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean enabled_rw() {
|
||||
return DeviceConfig.getBoolean(
|
||||
"aconfig_test",
|
||||
"com.android.aconfig.test.enabled_rw",
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let expected_featureflags_content = r#"
|
||||
package com.android.aconfig.test;
|
||||
public interface FeatureFlags {
|
||||
boolean disabled_ro();
|
||||
boolean disabled_rw();
|
||||
boolean enabled_ro();
|
||||
boolean enabled_rw();
|
||||
}
|
||||
"#;
|
||||
let mut file_set = HashMap::from([
|
||||
("com/android/aconfig/test/Flags.java", expect_flags_content),
|
||||
("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
|
||||
("com/android/aconfig/test/FeatureFlags.java", expected_featureflags_content),
|
||||
]);
|
||||
|
||||
for file in generated_files {
|
||||
let file_path = file.path.to_str().unwrap();
|
||||
assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
|
||||
assert_eq!(
|
||||
None,
|
||||
crate::test::first_significant_code_diff(
|
||||
file_set.get(file_path).unwrap(),
|
||||
&String::from_utf8(file.contents.clone()).unwrap()
|
||||
),
|
||||
"File {} content is not correct",
|
||||
file_path
|
||||
);
|
||||
file_set.remove(file_path);
|
||||
}
|
||||
|
||||
assert!(file_set.is_empty());
|
||||
}
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@ pub fn create_cache(package: &str, declarations: Vec<Input>, values: Vec<Input>)
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
pub fn create_java_lib(cache: Cache) -> Result<OutputFile> {
|
||||
pub fn create_java_lib(cache: Cache) -> Result<Vec<OutputFile>> {
|
||||
generate_java_code(&cache)
|
||||
}
|
||||
|
||||
|
@@ -147,8 +147,10 @@ fn main() -> Result<()> {
|
||||
let file = fs::File::open(path)?;
|
||||
let cache = Cache::read_from_reader(file)?;
|
||||
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
|
||||
let generated_file = commands::create_java_lib(cache)?;
|
||||
write_output_file_realtive_to_dir(&dir, &generated_file)?;
|
||||
let generated_files = commands::create_java_lib(cache)?;
|
||||
generated_files
|
||||
.iter()
|
||||
.try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?;
|
||||
}
|
||||
Some(("create-cpp-lib", sub_matches)) => {
|
||||
let path = get_required_arg::<String>(sub_matches, "cache")?;
|
||||
|
7
tools/aconfig/templates/FeatureFlags.java.template
Normal file
7
tools/aconfig/templates/FeatureFlags.java.template
Normal file
@@ -0,0 +1,7 @@
|
||||
package {package_name};
|
||||
|
||||
public interface FeatureFlags \{
|
||||
{{ for item in class_elements}}
|
||||
boolean {item.method_name}();
|
||||
{{ endfor }}
|
||||
}
|
@@ -1,11 +1,12 @@
|
||||
package {package};
|
||||
{{ if readwrite }}
|
||||
package {package_name};
|
||||
{{ if is_read_write }}
|
||||
import android.provider.DeviceConfig;
|
||||
{{ endif }}
|
||||
public final class Flags \{
|
||||
public final class FeatureFlagsImpl implements FeatureFlags \{
|
||||
{{ for item in class_elements}}
|
||||
public static boolean {item.method_name}() \{
|
||||
{{ if item.readwrite- }}
|
||||
@Override
|
||||
public boolean {item.method_name}() \{
|
||||
{{ if item.is_read_write- }}
|
||||
return DeviceConfig.getBoolean(
|
||||
"{item.device_config_namespace}",
|
||||
"{item.device_config_flag}",
|
||||
@@ -16,4 +17,4 @@ public final class Flags \{
|
||||
{{ -endif }}
|
||||
}
|
||||
{{ endfor }}
|
||||
}
|
||||
}
|
11
tools/aconfig/templates/Flags.java.template
Normal file
11
tools/aconfig/templates/Flags.java.template
Normal file
@@ -0,0 +1,11 @@
|
||||
package {package_name};
|
||||
|
||||
public final class Flags \{
|
||||
{{ for item in class_elements}}
|
||||
public static boolean {item.method_name}() \{
|
||||
return FEATURE_FLAGS.{item.method_name}();
|
||||
}
|
||||
{{ endfor }}
|
||||
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
|
||||
|
||||
}
|
Reference in New Issue
Block a user