Merge "aconfig: Java codegen iteration 1" am: 06377d79ab
am: 678166cbdb
Original change: https://android-review.googlesource.com/c/platform/build/+/2619534 Change-Id: I560b61509f4edd7dc9beb51c23519f89a256ee64 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::codegen;
|
||||||
use crate::commands::OutputFile;
|
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 package = cache.package();
|
||||||
let class_elements: Vec<ClassElement> =
|
let class_elements: Vec<ClassElement> =
|
||||||
cache.iter().map(|item| create_class_element(package, item)).collect();
|
cache.iter().map(|item| create_class_element(package, item)).collect();
|
||||||
let readwrite = class_elements.iter().any(|item| item.readwrite);
|
let is_read_write = class_elements.iter().any(|item| item.is_read_write);
|
||||||
let context = Context { package: package.to_string(), readwrite, class_elements };
|
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();
|
let mut template = TinyTemplate::new();
|
||||||
template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
|
template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
|
||||||
let contents = template.render("java_code_gen", &context)?;
|
template.add_template(
|
||||||
let mut path: PathBuf = package.split('.').collect();
|
"FeatureFlagsImpl.java",
|
||||||
// TODO: Allow customization of the java class name
|
include_str!("../templates/FeatureFlagsImpl.java.template"),
|
||||||
path.push("Flags.java");
|
)?;
|
||||||
Ok(OutputFile { contents: contents.into(), path })
|
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)]
|
#[derive(Serialize)]
|
||||||
struct Context {
|
struct Context {
|
||||||
pub package: String,
|
pub package_name: String,
|
||||||
pub readwrite: bool,
|
pub is_read_write: bool,
|
||||||
pub class_elements: Vec<ClassElement>,
|
pub class_elements: Vec<ClassElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct ClassElement {
|
struct ClassElement {
|
||||||
pub method_name: String,
|
|
||||||
pub readwrite: bool,
|
|
||||||
pub default_value: String,
|
pub default_value: String,
|
||||||
pub device_config_namespace: String,
|
pub device_config_namespace: String,
|
||||||
pub device_config_flag: 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 {
|
fn create_class_element(package: &str, item: &Item) -> ClassElement {
|
||||||
let device_config_flag = codegen::create_device_config_ident(package, &item.name)
|
let device_config_flag = codegen::create_device_config_ident(package, &item.name)
|
||||||
.expect("values checked at cache creation time");
|
.expect("values checked at cache creation time");
|
||||||
ClassElement {
|
ClassElement {
|
||||||
method_name: item.name.replace('-', "_"),
|
|
||||||
readwrite: item.permission == Permission::ReadWrite,
|
|
||||||
default_value: if item.state == FlagState::Enabled {
|
default_value: if item.state == FlagState::Enabled {
|
||||||
"true".to_string()
|
"true".to_string()
|
||||||
} else {
|
} else {
|
||||||
@@ -68,78 +84,100 @@ fn create_class_element(package: &str, item: &Item) -> ClassElement {
|
|||||||
},
|
},
|
||||||
device_config_namespace: item.namespace.clone(),
|
device_config_namespace: item.namespace.clone(),
|
||||||
device_config_flag,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::aconfig::{FlagDeclaration, FlagValue};
|
use std::collections::HashMap;
|
||||||
use crate::cache::CacheBuilder;
|
|
||||||
use crate::commands::Source;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generate_java_code() {
|
fn test_generate_java_code() {
|
||||||
let package = "com.example";
|
let cache = crate::test::create_cache();
|
||||||
let mut builder = CacheBuilder::new(package.to_string()).unwrap();
|
let generated_files = generate_java_code(&cache).unwrap();
|
||||||
builder
|
let expect_flags_content = r#"
|
||||||
.add_flag_declaration(
|
package com.android.aconfig.test;
|
||||||
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;
|
|
||||||
|
|
||||||
public final class Flags {
|
public final class Flags {
|
||||||
|
public static boolean disabled_ro() {
|
||||||
public static boolean test() {
|
return FEATURE_FLAGS.disabled_ro();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
public static boolean disabled_rw() {
|
||||||
public static boolean test2() {
|
return FEATURE_FLAGS.disabled_rw();
|
||||||
return DeviceConfig.getBoolean(
|
|
||||||
"ns",
|
|
||||||
"com.example.test2",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
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();
|
let expected_featureflagsimpl_content = r#"
|
||||||
assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
|
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!(
|
assert_eq!(
|
||||||
None,
|
None,
|
||||||
crate::test::first_significant_code_diff(
|
crate::test::first_significant_code_diff(
|
||||||
expect_content,
|
file_set.get(file_path).unwrap(),
|
||||||
&String::from_utf8(file.contents).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())
|
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)
|
generate_java_code(&cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -147,8 +147,10 @@ fn main() -> Result<()> {
|
|||||||
let file = fs::File::open(path)?;
|
let file = fs::File::open(path)?;
|
||||||
let cache = Cache::read_from_reader(file)?;
|
let cache = Cache::read_from_reader(file)?;
|
||||||
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
|
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
|
||||||
let generated_file = commands::create_java_lib(cache)?;
|
let generated_files = commands::create_java_lib(cache)?;
|
||||||
write_output_file_realtive_to_dir(&dir, &generated_file)?;
|
generated_files
|
||||||
|
.iter()
|
||||||
|
.try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?;
|
||||||
}
|
}
|
||||||
Some(("create-cpp-lib", sub_matches)) => {
|
Some(("create-cpp-lib", sub_matches)) => {
|
||||||
let path = get_required_arg::<String>(sub_matches, "cache")?;
|
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};
|
package {package_name};
|
||||||
{{ if readwrite }}
|
{{ if is_read_write }}
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
{{ endif }}
|
{{ endif }}
|
||||||
public final class Flags \{
|
public final class FeatureFlagsImpl implements FeatureFlags \{
|
||||||
{{ for item in class_elements}}
|
{{ for item in class_elements}}
|
||||||
public static boolean {item.method_name}() \{
|
@Override
|
||||||
{{ if item.readwrite- }}
|
public boolean {item.method_name}() \{
|
||||||
|
{{ if item.is_read_write- }}
|
||||||
return DeviceConfig.getBoolean(
|
return DeviceConfig.getBoolean(
|
||||||
"{item.device_config_namespace}",
|
"{item.device_config_namespace}",
|
||||||
"{item.device_config_flag}",
|
"{item.device_config_flag}",
|
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