Merge "Fix bug when memtag_heap in diag but not sanitizers"
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 6ba539c..5dba2d1 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -154,6 +154,10 @@
$(call add_soong_config_var_value,ANDROID,avf_enabled,$(PRODUCT_AVF_ENABLED))
endif
+ifdef PRODUCT_AVF_KERNEL_MODULES_ENABLED
+$(call add_soong_config_var_value,ANDROID,avf_kernel_modules_enabled,$(PRODUCT_AVF_KERNEL_MODULES_ENABLED))
+endif
+
# Enable system_server optimizations by default unless explicitly set or if
# there may be dependent runtime jars.
# TODO(b/240588226): Remove the off-by-default exceptions after handling
diff --git a/core/product.mk b/core/product.mk
index 818aac2..6f54b78 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -389,6 +389,9 @@
# If true, installs a full version of com.android.virt APEX.
_product_single_value_vars += PRODUCT_AVF_ENABLED
+# If true, kernel with modules will be used for Microdroid VMs.
+_product_single_value_vars += PRODUCT_AVF_KERNEL_MODULES_ENABLED
+
# List of .json files to be merged/compiled into vendor/etc/linker.config.pb
_product_list_vars += PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp
index 9617e0e..b8cce29 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/Android.bp
@@ -35,4 +35,7 @@
rust_test_host {
name: "aconfig.test",
defaults: ["aconfig.defaults"],
+ rustlibs: [
+ "libitertools",
+ ],
}
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 8517dd2..b3c73b8 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -18,3 +18,6 @@
[build-dependencies]
protobuf-codegen = "3.2.0"
+
+[dev-dependencies]
+itertools = "0.10.5"
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 9d36a9e..9f6424f 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -36,16 +36,17 @@
message flag_declaration {
required string name = 1;
- required string description = 2;
+ required string namespace = 2;
+ required string description = 3;
};
message flag_declarations {
- required string namespace = 1;
+ required string package = 1;
repeated flag_declaration flag = 2;
};
message flag_value {
- required string namespace = 1;
+ required string package = 1;
required string name = 2;
required flag_state state = 3;
required flag_permission permission = 4;
@@ -65,12 +66,13 @@
}
message parsed_flag {
- required string namespace = 1;
+ required string package = 1;
required string name = 2;
- required string description = 3;
- required flag_state state = 4;
- required flag_permission permission = 5;
- repeated tracepoint trace = 6;
+ required string namespace = 3;
+ required string description = 4;
+ required flag_state state = 5;
+ required flag_permission permission = 6;
+ repeated tracepoint trace = 7;
}
message parsed_flags {
diff --git a/tools/aconfig/src/aconfig.rs b/tools/aconfig/src/aconfig.rs
index b9fa324..5e7c861 100644
--- a/tools/aconfig/src/aconfig.rs
+++ b/tools/aconfig/src/aconfig.rs
@@ -81,6 +81,7 @@
#[derive(Debug, PartialEq, Eq)]
pub struct FlagDeclaration {
pub name: String,
+ pub namespace: String,
pub description: String,
}
@@ -100,16 +101,19 @@
let Some(name) = proto.name else {
bail!("missing 'name' field");
};
+ let Some(namespace) = proto.namespace else {
+ bail!("missing 'namespace' field");
+ };
let Some(description) = proto.description else {
bail!("missing 'description' field");
};
- Ok(FlagDeclaration { name, description })
+ Ok(FlagDeclaration { name, namespace, description })
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct FlagDeclarations {
- pub namespace: String,
+ pub package: String,
pub flags: Vec<FlagDeclaration>,
}
@@ -117,20 +121,20 @@
pub fn try_from_text_proto(text_proto: &str) -> Result<FlagDeclarations> {
let proto: ProtoFlagDeclarations = crate::protos::try_from_text_proto(text_proto)
.with_context(|| text_proto.to_owned())?;
- let Some(namespace) = proto.namespace else {
- bail!("missing 'namespace' field");
+ let Some(package) = proto.package else {
+ bail!("missing 'package' field");
};
let mut flags = vec![];
for proto_flag in proto.flag.into_iter() {
flags.push(proto_flag.try_into()?);
}
- Ok(FlagDeclarations { namespace, flags })
+ Ok(FlagDeclarations { package, flags })
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct FlagValue {
- pub namespace: String,
+ pub package: String,
pub name: String,
pub state: FlagState,
pub permission: Permission,
@@ -153,8 +157,8 @@
type Error = Error;
fn try_from(proto: ProtoFlagValue) -> Result<Self, Self::Error> {
- let Some(namespace) = proto.namespace else {
- bail!("missing 'namespace' field");
+ let Some(package) = proto.package else {
+ bail!("missing 'package' field");
};
let Some(name) = proto.name else {
bail!("missing 'name' field");
@@ -167,7 +171,7 @@
bail!("missing 'permission' field");
};
let permission = proto_permission.try_into()?;
- Ok(FlagValue { namespace, name, state, permission })
+ Ok(FlagValue { package, name, state, permission })
}
}
@@ -184,8 +188,9 @@
impl From<Item> for ProtoParsedFlag {
fn from(item: Item) -> Self {
let mut proto = crate::protos::ProtoParsedFlag::new();
- proto.set_namespace(item.namespace.to_owned());
+ proto.set_package(item.package.to_owned());
proto.set_name(item.name.clone());
+ proto.set_namespace(item.namespace.clone());
proto.set_description(item.description.clone());
proto.set_state(item.state.into());
proto.set_permission(item.permission.into());
@@ -214,11 +219,13 @@
fn test_flag_try_from_text_proto() {
let expected = FlagDeclaration {
name: "1234".to_owned(),
+ namespace: "ns".to_owned(),
description: "Description of the flag".to_owned(),
};
let s = r#"
name: "1234"
+ namespace: "ns"
description: "Description of the flag"
"#;
let actual = FlagDeclaration::try_from_text_proto(s).unwrap();
@@ -242,23 +249,33 @@
}
#[test]
- fn test_namespace_try_from_text_proto() {
+ fn test_package_try_from_text_proto() {
let expected = FlagDeclarations {
- namespace: "ns".to_owned(),
+ package: "com.example".to_owned(),
flags: vec![
- FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
- FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
+ FlagDeclaration {
+ name: "a".to_owned(),
+ namespace: "ns".to_owned(),
+ description: "A".to_owned(),
+ },
+ FlagDeclaration {
+ name: "b".to_owned(),
+ namespace: "ns".to_owned(),
+ description: "B".to_owned(),
+ },
],
};
let s = r#"
- namespace: "ns"
+ package: "com.example"
flag {
name: "a"
+ namespace: "ns"
description: "A"
}
flag {
name: "b"
+ namespace: "ns"
description: "B"
}
"#;
@@ -270,14 +287,14 @@
#[test]
fn test_flag_declaration_try_from_text_proto_list() {
let expected = FlagValue {
- namespace: "ns".to_owned(),
+ package: "com.example".to_owned(),
name: "1234".to_owned(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
};
let s = r#"
- namespace: "ns"
+ package: "com.example"
name: "1234"
state: ENABLED
permission: READ_ONLY
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index 972ba41..dd54480 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -34,12 +34,13 @@
#[derive(Serialize, Deserialize, Debug)]
pub struct Item {
- // TODO: duplicating the Cache.namespace as Item.namespace makes the internal representation
+ // TODO: duplicating the Cache.package as Item.package makes the internal representation
// closer to the proto message `parsed_flag`; hopefully this will enable us to replace the Item
- // struct and use a newtype instead once aconfig has matured. Until then, namespace should
+ // struct and use a newtype instead once aconfig has matured. Until then, package should
// really be a Cow<String>.
- pub namespace: String,
+ pub package: String,
pub name: String,
+ pub namespace: String,
pub description: String,
pub state: FlagState,
pub permission: Permission,
@@ -48,7 +49,7 @@
#[derive(Serialize, Deserialize, Debug)]
pub struct Cache {
- namespace: String,
+ package: String,
items: Vec<Item>,
}
@@ -96,9 +97,9 @@
self.items.into_iter()
}
- pub fn namespace(&self) -> &str {
- debug_assert!(!self.namespace.is_empty());
- &self.namespace
+ pub fn package(&self) -> &str {
+ debug_assert!(!self.package.is_empty());
+ &self.package
}
}
@@ -108,9 +109,9 @@
}
impl CacheBuilder {
- pub fn new(namespace: String) -> Result<CacheBuilder> {
- ensure!(codegen::is_valid_identifier(&namespace), "bad namespace");
- let cache = Cache { namespace, items: vec![] };
+ pub fn new(package: String) -> Result<CacheBuilder> {
+ ensure!(codegen::is_valid_package_ident(&package), "bad package");
+ let cache = Cache { package, items: vec![] };
Ok(CacheBuilder { cache })
}
@@ -119,7 +120,8 @@
source: Source,
declaration: FlagDeclaration,
) -> Result<&mut CacheBuilder> {
- ensure!(codegen::is_valid_identifier(&declaration.name), "bad flag name");
+ ensure!(codegen::is_valid_name_ident(&declaration.name), "bad flag name");
+ ensure!(codegen::is_valid_name_ident(&declaration.namespace), "bad namespace");
ensure!(!declaration.description.is_empty(), "empty flag description");
ensure!(
self.cache.items.iter().all(|item| item.name != declaration.name),
@@ -128,8 +130,9 @@
source
);
self.cache.items.push(Item {
- namespace: self.cache.namespace.clone(),
+ package: self.cache.package.clone(),
name: declaration.name.clone(),
+ namespace: declaration.namespace.clone(),
description: declaration.description,
state: DEFAULT_FLAG_STATE,
permission: DEFAULT_FLAG_PERMISSION,
@@ -147,18 +150,18 @@
source: Source,
value: FlagValue,
) -> Result<&mut CacheBuilder> {
- ensure!(codegen::is_valid_identifier(&value.namespace), "bad flag namespace");
- ensure!(codegen::is_valid_identifier(&value.name), "bad flag name");
+ ensure!(codegen::is_valid_package_ident(&value.package), "bad flag package");
+ ensure!(codegen::is_valid_name_ident(&value.name), "bad flag name");
ensure!(
- value.namespace == self.cache.namespace,
- "failed to set values for flag {}/{} from {}: expected namespace {}",
- value.namespace,
+ value.package == self.cache.package,
+ "failed to set values for flag {}/{} from {}: expected package {}",
+ value.package,
value.name,
source,
- self.cache.namespace
+ self.cache.package
);
let Some(existing_item) = self.cache.items.iter_mut().find(|item| item.name == value.name) else {
- bail!("failed to set values for flag {}/{} from {}: flag not declared", value.namespace, value.name, source);
+ bail!("failed to set values for flag {}/{} from {}: flag not declared", value.package, value.name, source);
};
existing_item.state = value.state;
existing_item.permission = value.permission;
@@ -182,17 +185,25 @@
#[test]
fn test_add_flag_declaration() {
- let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+ let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
builder
.add_flag_declaration(
Source::File("first.txt".to_string()),
- FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ FlagDeclaration {
+ name: "foo".to_string(),
+ namespace: "ns".to_string(),
+ description: "desc".to_string(),
+ },
)
.unwrap();
let error = builder
.add_flag_declaration(
Source::File("second.txt".to_string()),
- FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ FlagDeclaration {
+ name: "foo".to_string(),
+ namespace: "ns".to_string(),
+ description: "desc".to_string(),
+ },
)
.unwrap_err();
assert_eq!(
@@ -202,7 +213,11 @@
builder
.add_flag_declaration(
Source::File("first.txt".to_string()),
- FlagDeclaration { name: "bar".to_string(), description: "desc".to_string() },
+ FlagDeclaration {
+ name: "bar".to_string(),
+ namespace: "ns".to_string(),
+ description: "desc".to_string(),
+ },
)
.unwrap();
@@ -217,12 +232,12 @@
#[test]
fn test_add_flag_value() {
- let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+ let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
let error = builder
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: "ns".to_string(),
+ package: "com.example".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
@@ -231,13 +246,17 @@
.unwrap_err();
assert_eq!(
&format!("{:?}", error),
- "failed to set values for flag ns/foo from <memory>: flag not declared"
+ "failed to set values for flag com.example/foo from <memory>: flag not declared"
);
builder
.add_flag_declaration(
Source::File("first.txt".to_string()),
- FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ FlagDeclaration {
+ name: "foo".to_string(),
+ namespace: "ns".to_string(),
+ description: "desc".to_string(),
+ },
)
.unwrap();
@@ -245,7 +264,7 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: "ns".to_string(),
+ package: "com.example".to_string(),
name: "foo".to_string(),
state: FlagState::Disabled,
permission: Permission::ReadOnly,
@@ -257,7 +276,7 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: "ns".to_string(),
+ package: "com.example".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadWrite,
@@ -265,19 +284,19 @@
)
.unwrap();
- // different namespace -> no-op
+ // different package -> no-op
let error = builder
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: "some_other_namespace".to_string(),
+ package: "some_other_package".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
},
)
.unwrap_err();
- assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_namespace/foo from <memory>: expected namespace ns");
+ assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_package/foo from <memory>: expected package com.example");
let cache = builder.build();
let item = cache.iter().find(|&item| item.name == "foo").unwrap();
@@ -286,18 +305,22 @@
}
#[test]
- fn test_reject_empty_cache_namespace() {
+ fn test_reject_empty_cache_package() {
CacheBuilder::new("".to_string()).unwrap_err();
}
#[test]
fn test_reject_empty_flag_declaration_fields() {
- let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+ let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
let error = builder
.add_flag_declaration(
Source::Memory,
- FlagDeclaration { name: "".to_string(), description: "Description".to_string() },
+ FlagDeclaration {
+ name: "".to_string(),
+ namespace: "ns".to_string(),
+ description: "Description".to_string(),
+ },
)
.unwrap_err();
assert_eq!(&format!("{:?}", error), "bad flag name");
@@ -305,7 +328,11 @@
let error = builder
.add_flag_declaration(
Source::Memory,
- FlagDeclaration { name: "foo".to_string(), description: "".to_string() },
+ FlagDeclaration {
+ name: "foo".to_string(),
+ namespace: "ns".to_string(),
+ description: "".to_string(),
+ },
)
.unwrap_err();
assert_eq!(&format!("{:?}", error), "empty flag description");
@@ -313,11 +340,15 @@
#[test]
fn test_reject_empty_flag_value_files() {
- let mut builder = CacheBuilder::new("ns".to_string()).unwrap();
+ let mut builder = CacheBuilder::new("com.example".to_string()).unwrap();
builder
.add_flag_declaration(
Source::Memory,
- FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
+ FlagDeclaration {
+ name: "foo".to_string(),
+ namespace: "ns".to_string(),
+ description: "desc".to_string(),
+ },
)
.unwrap();
@@ -325,20 +356,20 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: "".to_string(),
+ package: "".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
},
)
.unwrap_err();
- assert_eq!(&format!("{:?}", error), "bad flag namespace");
+ assert_eq!(&format!("{:?}", error), "bad flag package");
let error = builder
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: "ns".to_string(),
+ package: "com.example".to_string(),
name: "".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
diff --git a/tools/aconfig/src/codegen.rs b/tools/aconfig/src/codegen.rs
index b60ec51..fea9961 100644
--- a/tools/aconfig/src/codegen.rs
+++ b/tools/aconfig/src/codegen.rs
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-pub fn is_valid_identifier(s: &str) -> bool {
+use anyhow::{ensure, Result};
+
+pub fn is_valid_name_ident(s: &str) -> bool {
// Identifiers must match [a-z][a-z0-9_]*
let mut chars = s.chars();
let Some(first) = chars.next() else {
@@ -26,18 +28,54 @@
chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
}
+pub fn is_valid_package_ident(s: &str) -> bool {
+ s.split('.').all(is_valid_name_ident)
+}
+
+pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> {
+ ensure!(is_valid_package_ident(package), "bad package");
+ ensure!(is_valid_package_ident(flag_name), "bad flag name");
+ Ok(format!("{}.{}", package, flag_name))
+}
+
#[cfg(test)]
mod tests {
use super::*;
#[test]
- fn test_is_valid_identifier() {
- assert!(is_valid_identifier("foo"));
- assert!(is_valid_identifier("foo_bar_123"));
+ fn test_is_valid_name_ident() {
+ assert!(is_valid_name_ident("foo"));
+ assert!(is_valid_name_ident("foo_bar_123"));
- assert!(!is_valid_identifier(""));
- assert!(!is_valid_identifier("123_foo"));
- assert!(!is_valid_identifier("foo-bar"));
- assert!(!is_valid_identifier("foo-b\u{00e5}r"));
+ assert!(!is_valid_name_ident(""));
+ assert!(!is_valid_name_ident("123_foo"));
+ assert!(!is_valid_name_ident("foo-bar"));
+ assert!(!is_valid_name_ident("foo-b\u{00e5}r"));
+ }
+
+ #[test]
+ fn test_is_valid_package_ident() {
+ assert!(is_valid_package_ident("foo"));
+ assert!(is_valid_package_ident("foo_bar_123"));
+ assert!(is_valid_package_ident("foo.bar"));
+ assert!(is_valid_package_ident("foo.bar.a123"));
+
+ assert!(!is_valid_package_ident(""));
+ assert!(!is_valid_package_ident("123_foo"));
+ assert!(!is_valid_package_ident("foo-bar"));
+ assert!(!is_valid_package_ident("foo-b\u{00e5}r"));
+ assert!(!is_valid_package_ident("foo.bar.123"));
+ assert!(!is_valid_package_ident(".foo.bar"));
+ assert!(!is_valid_package_ident("foo.bar."));
+ assert!(!is_valid_package_ident("."));
+ assert!(!is_valid_package_ident("foo..bar"));
+ }
+
+ #[test]
+ fn test_create_device_config_ident() {
+ assert_eq!(
+ "com.foo.bar.some_flag",
+ create_device_config_ident("com.foo.bar", "some_flag").unwrap()
+ );
}
}
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
index 2aeea6a..2c32bb0 100644
--- a/tools/aconfig/src/codegen_cpp.rs
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -14,29 +14,42 @@
* limitations under the License.
*/
-use anyhow::Result;
+use anyhow::{ensure, Result};
use serde::Serialize;
use tinytemplate::TinyTemplate;
use crate::aconfig::{FlagState, Permission};
use crate::cache::{Cache, Item};
+use crate::codegen;
use crate::commands::OutputFile;
pub fn generate_cpp_code(cache: &Cache) -> Result<OutputFile> {
- let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+ 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 namespace = cache.namespace().to_lowercase();
- let context = Context { namespace: namespace.clone(), readwrite, class_elements };
+ let header = package.replace('.', "_");
+ let cpp_namespace = package.replace('.', "::");
+ ensure!(codegen::is_valid_name_ident(&header));
+ let context = Context {
+ header: header.clone(),
+ cpp_namespace,
+ package: package.to_string(),
+ readwrite,
+ class_elements,
+ };
let mut template = TinyTemplate::new();
template.add_template("cpp_code_gen", include_str!("../templates/cpp.template"))?;
let contents = template.render("cpp_code_gen", &context)?;
- let path = ["aconfig", &(namespace + ".h")].iter().collect();
+ let path = ["aconfig", &(header + ".h")].iter().collect();
Ok(OutputFile { contents: contents.into(), path })
}
#[derive(Serialize)]
struct Context {
- pub namespace: String,
+ pub header: String,
+ pub cpp_namespace: String,
+ pub package: String,
pub readwrite: bool,
pub class_elements: Vec<ClassElement>,
}
@@ -46,9 +59,11 @@
pub readwrite: bool,
pub default_value: String,
pub flag_name: String,
+ pub device_config_namespace: String,
+ pub device_config_flag: String,
}
-fn create_class_element(item: &Item) -> ClassElement {
+fn create_class_element(package: &str, item: &Item) -> ClassElement {
ClassElement {
readwrite: item.permission == Permission::ReadWrite,
default_value: if item.state == FlagState::Enabled {
@@ -57,6 +72,9 @@
"false".to_string()
},
flag_name: item.name.clone(),
+ device_config_namespace: item.namespace.to_string(),
+ device_config_flag: codegen::create_device_config_ident(package, &item.name)
+ .expect("values checked at cache creation time"),
}
}
@@ -69,13 +87,14 @@
#[test]
fn test_cpp_codegen_build_time_flag_only() {
- let namespace = "my_namespace";
- let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+ let package = "com.example";
+ let mut builder = CacheBuilder::new(package.to_string()).unwrap();
builder
.add_flag_declaration(
Source::File("aconfig_one.txt".to_string()),
FlagDeclaration {
name: "my_flag_one".to_string(),
+ namespace: "ns".to_string(),
description: "buildtime disable".to_string(),
},
)
@@ -83,7 +102,7 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: namespace.to_string(),
+ package: package.to_string(),
name: "my_flag_one".to_string(),
state: FlagState::Disabled,
permission: Permission::ReadOnly,
@@ -94,6 +113,7 @@
Source::File("aconfig_two.txt".to_string()),
FlagDeclaration {
name: "my_flag_two".to_string(),
+ namespace: "ns".to_string(),
description: "buildtime enable".to_string(),
},
)
@@ -101,7 +121,7 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: namespace.to_string(),
+ package: package.to_string(),
name: "my_flag_two".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
@@ -109,11 +129,10 @@
)
.unwrap();
let cache = builder.build();
- let expect_content = r#"#ifndef my_namespace_HEADER_H
- #define my_namespace_HEADER_H
- #include "my_namespace.h"
+ let expect_content = r#"#ifndef com_example_HEADER_H
+ #define com_example_HEADER_H
- namespace my_namespace {
+ namespace com::example {
class my_flag_one {
public:
@@ -133,7 +152,7 @@
#endif
"#;
let file = generate_cpp_code(&cache).unwrap();
- assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
+ assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
assert_eq!(
expect_content.replace(' ', ""),
String::from_utf8(file.contents).unwrap().replace(' ', "")
@@ -142,13 +161,14 @@
#[test]
fn test_cpp_codegen_runtime_flag() {
- let namespace = "my_namespace";
- let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+ let package = "com.example";
+ let mut builder = CacheBuilder::new(package.to_string()).unwrap();
builder
.add_flag_declaration(
Source::File("aconfig_one.txt".to_string()),
FlagDeclaration {
name: "my_flag_one".to_string(),
+ namespace: "ns".to_string(),
description: "buildtime disable".to_string(),
},
)
@@ -157,6 +177,7 @@
Source::File("aconfig_two.txt".to_string()),
FlagDeclaration {
name: "my_flag_two".to_string(),
+ namespace: "ns".to_string(),
description: "runtime enable".to_string(),
},
)
@@ -164,7 +185,7 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: namespace.to_string(),
+ package: package.to_string(),
name: "my_flag_two".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadWrite,
@@ -172,21 +193,20 @@
)
.unwrap();
let cache = builder.build();
- let expect_content = r#"#ifndef my_namespace_HEADER_H
- #define my_namespace_HEADER_H
- #include "my_namespace.h"
+ let expect_content = r#"#ifndef com_example_HEADER_H
+ #define com_example_HEADER_H
#include <server_configurable_flags/get_flags.h>
using namespace server_configurable_flags;
- namespace my_namespace {
+ namespace com::example {
class my_flag_one {
public:
virtual const bool value() {
return GetServerConfigurableFlag(
- "my_namespace",
- "my_flag_one",
+ "ns",
+ "com.example.my_flag_one",
"false") == "true";
}
}
@@ -195,8 +215,8 @@
public:
virtual const bool value() {
return GetServerConfigurableFlag(
- "my_namespace",
- "my_flag_two",
+ "ns",
+ "com.example.my_flag_two",
"true") == "true";
}
}
@@ -205,10 +225,13 @@
#endif
"#;
let file = generate_cpp_code(&cache).unwrap();
- assert_eq!("aconfig/my_namespace.h", file.path.to_str().unwrap());
+ assert_eq!("aconfig/com_example.h", file.path.to_str().unwrap());
assert_eq!(
- expect_content.replace(' ', ""),
- String::from_utf8(file.contents).unwrap().replace(' ', "")
+ None,
+ crate::test::first_significant_code_diff(
+ expect_content,
+ &String::from_utf8(file.contents).unwrap()
+ )
);
}
}
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 98288e7..dfd6766 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -21,17 +21,19 @@
use crate::aconfig::{FlagState, Permission};
use crate::cache::{Cache, Item};
+use crate::codegen;
use crate::commands::OutputFile;
pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
- let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+ 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 namespace = cache.namespace();
- let context = Context { namespace: namespace.to_string(), readwrite, class_elements };
+ let context = Context { package: package.to_string(), readwrite, class_elements };
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 = ["aconfig", namespace].iter().collect();
+ 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 })
@@ -39,7 +41,7 @@
#[derive(Serialize)]
struct Context {
- pub namespace: String,
+ pub package: String,
pub readwrite: bool,
pub class_elements: Vec<ClassElement>,
}
@@ -49,11 +51,13 @@
pub method_name: String,
pub readwrite: bool,
pub default_value: String,
- pub feature_name: String,
- pub flag_name: String,
+ pub device_config_namespace: String,
+ pub device_config_flag: String,
}
-fn create_class_element(item: &Item) -> ClassElement {
+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.clone(),
readwrite: item.permission == Permission::ReadWrite,
@@ -62,8 +66,8 @@
} else {
"false".to_string()
},
- feature_name: item.name.clone(),
- flag_name: item.name.clone(),
+ device_config_namespace: item.namespace.clone(),
+ device_config_flag,
}
}
@@ -76,13 +80,14 @@
#[test]
fn test_generate_java_code() {
- let namespace = "example";
- let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
+ 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(),
},
)
@@ -91,6 +96,7 @@
Source::File("test2.txt".to_string()),
FlagDeclaration {
name: "test2".to_string(),
+ namespace: "ns".to_string(),
description: "runtime disable".to_string(),
},
)
@@ -98,7 +104,7 @@
.add_flag_value(
Source::Memory,
FlagValue {
- namespace: namespace.to_string(),
+ package: package.to_string(),
name: "test".to_string(),
state: FlagState::Disabled,
permission: Permission::ReadOnly,
@@ -106,7 +112,7 @@
)
.unwrap();
let cache = builder.build();
- let expect_content = r#"package aconfig.example;
+ let expect_content = r#"package com.example;
import android.provider.DeviceConfig;
@@ -118,8 +124,8 @@
public static boolean test2() {
return DeviceConfig.getBoolean(
- "example",
- "test2__test2",
+ "ns",
+ "com.example.test2",
false
);
}
@@ -127,10 +133,13 @@
}
"#;
let file = generate_java_code(&cache).unwrap();
- assert_eq!("aconfig/example/Flags.java", file.path.to_str().unwrap());
+ assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
assert_eq!(
- expect_content.replace(' ', ""),
- String::from_utf8(file.contents).unwrap().replace(' ', "")
+ None,
+ crate::test::first_significant_code_diff(
+ expect_content,
+ &String::from_utf8(file.contents).unwrap()
+ )
);
}
}
diff --git a/tools/aconfig/src/codegen_rust.rs b/tools/aconfig/src/codegen_rust.rs
index a48e464..98caeae 100644
--- a/tools/aconfig/src/codegen_rust.rs
+++ b/tools/aconfig/src/codegen_rust.rs
@@ -20,13 +20,18 @@
use crate::aconfig::{FlagState, Permission};
use crate::cache::{Cache, Item};
+use crate::codegen;
use crate::commands::OutputFile;
pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
- let namespace = cache.namespace();
+ let package = cache.package();
let parsed_flags: Vec<TemplateParsedFlag> =
- cache.iter().map(|item| create_template_parsed_flag(namespace, item)).collect();
- let context = TemplateContext { namespace: namespace.to_string(), parsed_flags };
+ cache.iter().map(|item| TemplateParsedFlag::new(package, item)).collect();
+ let context = TemplateContext {
+ package: package.to_string(),
+ parsed_flags,
+ modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
+ };
let mut template = TinyTemplate::new();
template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
let contents = template.render("rust_code_gen", &context)?;
@@ -36,14 +41,16 @@
#[derive(Serialize)]
struct TemplateContext {
- pub namespace: String,
+ pub package: String,
pub parsed_flags: Vec<TemplateParsedFlag>,
+ pub modules: Vec<String>,
}
#[derive(Serialize)]
struct TemplateParsedFlag {
pub name: String,
- pub fn_name: String,
+ pub device_config_namespace: String,
+ pub device_config_flag: String,
// TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
// Invariant: exactly one of these fields will be true
@@ -52,28 +59,32 @@
pub is_read_write: bool,
}
-#[allow(clippy::nonminimal_bool)]
-fn create_template_parsed_flag(namespace: &str, item: &Item) -> TemplateParsedFlag {
- let template = TemplateParsedFlag {
- name: item.name.clone(),
- fn_name: format!("{}_{}", namespace, &item.name),
- is_read_only_enabled: item.permission == Permission::ReadOnly
- && item.state == FlagState::Enabled,
- is_read_only_disabled: item.permission == Permission::ReadOnly
- && item.state == FlagState::Disabled,
- is_read_write: item.permission == Permission::ReadWrite,
- };
- #[rustfmt::skip]
- debug_assert!(
- (template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) ||
- (!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) ||
- (!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write),
- "TemplateParsedFlag invariant failed: {} {} {}",
- template.is_read_only_enabled,
- template.is_read_only_disabled,
- template.is_read_write,
- );
- template
+impl TemplateParsedFlag {
+ #[allow(clippy::nonminimal_bool)]
+ fn new(package: &str, item: &Item) -> Self {
+ let template = TemplateParsedFlag {
+ name: item.name.clone(),
+ device_config_namespace: item.namespace.to_string(),
+ device_config_flag: codegen::create_device_config_ident(package, &item.name)
+ .expect("values checked at cache creation time"),
+ is_read_only_enabled: item.permission == Permission::ReadOnly
+ && item.state == FlagState::Enabled,
+ is_read_only_disabled: item.permission == Permission::ReadOnly
+ && item.state == FlagState::Disabled,
+ is_read_write: item.permission == Permission::ReadWrite,
+ };
+ #[rustfmt::skip]
+ debug_assert!(
+ (template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) ||
+ (!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) ||
+ (!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write),
+ "TemplateParsedFlag invariant failed: {} {} {}",
+ template.is_read_only_enabled,
+ template.is_read_only_disabled,
+ template.is_read_write,
+ );
+ template
+ }
}
#[cfg(test)]
@@ -86,26 +97,41 @@
let generated = generate_rust_code(&cache).unwrap();
assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
let expected = r#"
+pub mod com {
+pub mod android {
+pub mod aconfig {
+pub mod test {
#[inline(always)]
-pub const fn r#test_disabled_ro() -> bool {
+pub const fn r#disabled_ro() -> bool {
false
}
#[inline(always)]
-pub fn r#test_disabled_rw() -> bool {
- flags_rust::GetServerConfigurableFlag("test", "disabled_rw", "false") == "true"
+pub fn r#disabled_rw() -> bool {
+ flags_rust::GetServerConfigurableFlag("aconfig_test", "com.android.aconfig.test.disabled_rw", "false") == "true"
}
#[inline(always)]
-pub const fn r#test_enabled_ro() -> bool {
+pub const fn r#enabled_ro() -> bool {
true
}
#[inline(always)]
-pub fn r#test_enabled_rw() -> bool {
- flags_rust::GetServerConfigurableFlag("test", "enabled_rw", "false") == "true"
+pub fn r#enabled_rw() -> bool {
+ flags_rust::GetServerConfigurableFlag("aconfig_test", "com.android.aconfig.test.enabled_rw", "false") == "true"
+}
+
+}
+}
+}
}
"#;
- assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());
+ assert_eq!(
+ None,
+ crate::test::first_significant_code_diff(
+ expected,
+ &String::from_utf8(generated.contents).unwrap()
+ )
+ );
}
}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 3ae72c6..586ba04 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -55,12 +55,8 @@
pub contents: Vec<u8>,
}
-pub fn create_cache(
- namespace: &str,
- declarations: Vec<Input>,
- values: Vec<Input>,
-) -> Result<Cache> {
- let mut builder = CacheBuilder::new(namespace.to_owned())?;
+pub fn create_cache(package: &str, declarations: Vec<Input>, values: Vec<Input>) -> Result<Cache> {
+ let mut builder = CacheBuilder::new(package.to_owned())?;
for mut input in declarations {
let mut contents = String::new();
@@ -68,11 +64,11 @@
let dec_list = FlagDeclarations::try_from_text_proto(&contents)
.with_context(|| format!("Failed to parse {}", input.source))?;
ensure!(
- namespace == dec_list.namespace,
- "Failed to parse {}: expected namespace {}, got {}",
+ package == dec_list.package,
+ "Failed to parse {}: expected package {}, got {}",
input.source,
- namespace,
- dec_list.namespace
+ package,
+ dec_list.package
);
for d in dec_list.flags.into_iter() {
builder.add_flag_declaration(input.source.clone(), d)?;
@@ -110,8 +106,9 @@
for item in sort_and_iter_items(caches).filter(|item| item.permission == Permission::ReadWrite)
{
let line = format!(
- "{}/{}:{}\n",
+ "{}:{}.{}={}\n",
item.namespace,
+ item.package,
item.name,
match item.state {
FlagState::Enabled => "enabled",
@@ -129,7 +126,7 @@
{
let line = format!(
"persist.device_config.{}.{}={}\n",
- item.namespace,
+ item.package,
item.name,
match item.state {
FlagState::Enabled => "true",
@@ -155,7 +152,7 @@
for item in sort_and_iter_items(caches) {
let line = format!(
"{}/{}: {:?} {:?}\n",
- item.namespace, item.name, item.state, item.permission
+ item.package, item.name, item.state, item.permission
);
output.extend_from_slice(line.as_bytes());
}
@@ -181,7 +178,7 @@
}
fn sort_and_iter_caches(mut caches: Vec<Cache>) -> impl Iterator<Item = Cache> {
- caches.sort_by_cached_key(|cache| cache.namespace().to_string());
+ caches.sort_by_cached_key(|cache| cache.package().to_string());
caches.into_iter()
}
@@ -190,55 +187,58 @@
use super::*;
use crate::aconfig::{FlagState, Permission};
- fn create_test_cache_ns1() -> Cache {
+ fn create_test_cache_com_example() -> Cache {
let s = r#"
- namespace: "ns1"
+ package: "com.example"
flag {
name: "a"
+ namespace: "ns"
description: "Description of a"
}
flag {
name: "b"
+ namespace: "ns"
description: "Description of b"
}
"#;
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
let o = r#"
flag_value {
- namespace: "ns1"
+ package: "com.example"
name: "a"
state: DISABLED
permission: READ_ONLY
}
"#;
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
- create_cache("ns1", declarations, values).unwrap()
+ create_cache("com.example", declarations, values).unwrap()
}
- fn create_test_cache_ns2() -> Cache {
+ fn create_test_cache_com_other() -> Cache {
let s = r#"
- namespace: "ns2"
+ package: "com.other"
flag {
name: "c"
+ namespace: "ns"
description: "Description of c"
}
"#;
let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
let o = r#"
flag_value {
- namespace: "ns2"
+ package: "com.other"
name: "c"
state: DISABLED
permission: READ_ONLY
}
"#;
let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
- create_cache("ns2", declarations, values).unwrap()
+ create_cache("com.other", declarations, values).unwrap()
}
#[test]
fn test_create_cache() {
- let caches = create_test_cache_ns1(); // calls create_cache
+ let caches = create_test_cache_com_example(); // calls create_cache
let item = caches.iter().find(|&item| item.name == "a").unwrap();
assert_eq!(FlagState::Disabled, item.state);
assert_eq!(Permission::ReadOnly, item.permission);
@@ -249,7 +249,7 @@
let caches = vec![crate::test::create_cache()];
let bytes = create_device_config_defaults(caches).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
- assert_eq!("test/disabled_rw:disabled\ntest/enabled_rw:enabled\n", text);
+ assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text);
}
#[test]
@@ -257,12 +257,12 @@
let caches = vec![crate::test::create_cache()];
let bytes = create_device_config_sysprops(caches).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
- assert_eq!("persist.device_config.test.disabled_rw=false\npersist.device_config.test.enabled_rw=true\n", text);
+ assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
}
#[test]
fn test_dump_text_format() {
- let caches = vec![create_test_cache_ns1()];
+ let caches = vec![create_test_cache_com_example()];
let bytes = dump_cache(caches, DumpFormat::Text).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
assert!(text.contains("a: Disabled"));
@@ -273,7 +273,7 @@
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
use protobuf::Message;
- let caches = vec![create_test_cache_ns1()];
+ let caches = vec![create_test_cache_com_example()];
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
@@ -284,7 +284,7 @@
let item =
actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
- assert_eq!(item.namespace(), "ns1");
+ assert_eq!(item.package(), "com.example");
assert_eq!(item.name(), "b");
assert_eq!(item.description(), "Description of b");
assert_eq!(item.state(), ProtoFlagState::DISABLED);
@@ -298,18 +298,22 @@
#[test]
fn test_dump_multiple_caches() {
- let caches = vec![create_test_cache_ns1(), create_test_cache_ns2()];
+ let caches = vec![create_test_cache_com_example(), create_test_cache_com_other()];
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
let dump = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
assert_eq!(
dump.parsed_flag
.iter()
- .map(|parsed_flag| format!("{}/{}", parsed_flag.namespace(), parsed_flag.name()))
+ .map(|parsed_flag| format!("{}/{}", parsed_flag.package(), parsed_flag.name()))
.collect::<Vec<_>>(),
- vec!["ns1/a".to_string(), "ns1/b".to_string(), "ns2/c".to_string()]
+ vec![
+ "com.example/a".to_string(),
+ "com.example/b".to_string(),
+ "com.other/c".to_string()
+ ]
);
- let caches = vec![create_test_cache_ns2(), create_test_cache_ns1()];
+ let caches = vec![create_test_cache_com_other(), create_test_cache_com_example()];
let bytes = dump_cache(caches, DumpFormat::Protobuf).unwrap();
let dump_reversed_input = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
assert_eq!(dump, dump_reversed_input);
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index dab0191..5a820d9 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -44,7 +44,7 @@
.subcommand_required(true)
.subcommand(
Command::new("create-cache")
- .arg(Arg::new("namespace").long("namespace").required(true))
+ .arg(Arg::new("package").long("package").required(true))
.arg(Arg::new("declarations").long("declarations").action(ArgAction::Append))
.arg(Arg::new("values").long("values").action(ArgAction::Append))
.arg(Arg::new("cache").long("cache").required(true)),
@@ -134,10 +134,10 @@
let matches = cli().get_matches();
match matches.subcommand() {
Some(("create-cache", sub_matches)) => {
- let namespace = get_required_arg::<String>(sub_matches, "namespace")?;
+ let package = get_required_arg::<String>(sub_matches, "package")?;
let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
let values = open_zero_or_more_files(sub_matches, "values")?;
- let cache = commands::create_cache(namespace, declarations, values)?;
+ let cache = commands::create_cache(package, declarations, values)?;
let path = get_required_arg::<String>(sub_matches, "cache")?;
let file = fs::File::create(path)?;
cache.write_to_writer(file)?;
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/src/test.rs
index 621381a..cc19c6a 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/src/test.rs
@@ -18,10 +18,11 @@
pub mod test_utils {
use crate::cache::Cache;
use crate::commands::{Input, Source};
+ use itertools;
pub fn create_cache() -> Cache {
crate::commands::create_cache(
- "test",
+ "com.android.aconfig.test",
vec![Input {
source: Source::File("testdata/test.aconfig".to_string()),
reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),
@@ -39,6 +40,54 @@
)
.unwrap()
}
+
+ pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {
+ let a = a.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
+ let b = b.lines().map(|line| line.trim_start()).filter(|line| !line.is_empty());
+ match itertools::diff_with(a, b, |left, right| left == right) {
+ Some(itertools::Diff::FirstMismatch(_, mut left, mut right)) => {
+ Some(format!("'{}' vs '{}'", left.next().unwrap(), right.next().unwrap()))
+ }
+ Some(itertools::Diff::Shorter(_, mut left)) => {
+ Some(format!("LHS trailing data: '{}'", left.next().unwrap()))
+ }
+ Some(itertools::Diff::Longer(_, mut right)) => {
+ Some(format!("RHS trailing data: '{}'", right.next().unwrap()))
+ }
+ None => None,
+ }
+ }
+
+ #[test]
+ fn test_first_significant_code_diff() {
+ assert!(first_significant_code_diff("", "").is_none());
+ assert!(first_significant_code_diff(" a", "\n\na\n").is_none());
+ let a = r#"
+ public class A {
+ private static final String FOO = "FOO";
+ public static void main(String[] args) {
+ System.out.println("FOO=" + FOO);
+ }
+ }
+ "#;
+ let b = r#"
+ public class A {
+ private static final String FOO = "BAR";
+ public static void main(String[] args) {
+ System.out.println("foo=" + FOO);
+ }
+ }
+ "#;
+ assert_eq!(Some(r#"'private static final String FOO = "FOO";' vs 'private static final String FOO = "BAR";'"#.to_string()), first_significant_code_diff(a, b));
+ assert_eq!(
+ Some("LHS trailing data: 'b'".to_string()),
+ first_significant_code_diff("a\nb", "a")
+ );
+ assert_eq!(
+ Some("RHS trailing data: 'b'".to_string()),
+ first_significant_code_diff("a", "a\nb")
+ );
+ }
}
#[cfg(test)]
diff --git a/tools/aconfig/templates/cpp.template b/tools/aconfig/templates/cpp.template
index ae8b59f..93f9b80 100644
--- a/tools/aconfig/templates/cpp.template
+++ b/tools/aconfig/templates/cpp.template
@@ -1,19 +1,18 @@
-#ifndef {namespace}_HEADER_H
-#define {namespace}_HEADER_H
-#include "{namespace}.h"
+#ifndef {header}_HEADER_H
+#define {header}_HEADER_H
{{ if readwrite }}
#include <server_configurable_flags/get_flags.h>
using namespace server_configurable_flags;
{{ endif }}
-namespace {namespace} \{
+namespace {cpp_namespace} \{
{{ for item in class_elements}}
class {item.flag_name} \{
public:
virtual const bool value() \{
{{ if item.readwrite- }}
return GetServerConfigurableFlag(
- "{namespace}",
- "{item.flag_name}",
+ "{item.device_config_namespace}",
+ "{item.device_config_flag}",
"{item.default_value}") == "true";
{{ -else- }}
return {item.default_value};
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
index 30c7ad7..a3d3319 100644
--- a/tools/aconfig/templates/java.template
+++ b/tools/aconfig/templates/java.template
@@ -1,4 +1,4 @@
-package aconfig.{namespace};
+package {package};
{{ if readwrite }}
import android.provider.DeviceConfig;
{{ endif }}
@@ -7,8 +7,8 @@
public static boolean {item.method_name}() \{
{{ if item.readwrite- }}
return DeviceConfig.getBoolean(
- "{namespace}",
- "{item.feature_name}__{item.flag_name}",
+ "{item.device_config_namespace}",
+ "{item.device_config_flag}",
{item.default_value}
);
{{ -else- }}
diff --git a/tools/aconfig/templates/rust.template b/tools/aconfig/templates/rust.template
index d7f4e8d..d914943 100644
--- a/tools/aconfig/templates/rust.template
+++ b/tools/aconfig/templates/rust.template
@@ -1,23 +1,29 @@
+{{- for mod in modules -}}
+pub mod {mod} \{
+{{ endfor -}}
{{- for parsed_flag in parsed_flags -}}
{{- if parsed_flag.is_read_only_disabled -}}
#[inline(always)]
-pub const fn r#{parsed_flag.fn_name}() -> bool \{
+pub const fn r#{parsed_flag.name}() -> bool \{
false
}
{{ endif -}}
{{- if parsed_flag.is_read_only_enabled -}}
#[inline(always)]
-pub const fn r#{parsed_flag.fn_name}() -> bool \{
+pub const fn r#{parsed_flag.name}() -> bool \{
true
}
{{ endif -}}
{{- if parsed_flag.is_read_write -}}
#[inline(always)]
-pub fn r#{parsed_flag.fn_name}() -> bool \{
- flags_rust::GetServerConfigurableFlag("{namespace}", "{parsed_flag.name}", "false") == "true"
+pub fn r#{parsed_flag.name}() -> bool \{
+ flags_rust::GetServerConfigurableFlag("{parsed_flag.device_config_namespace}", "{parsed_flag.device_config_flag}", "false") == "true"
}
{{ endif -}}
{{- endfor -}}
+{{- for mod in modules -}}
+}
+{{ endfor -}}
diff --git a/tools/aconfig/testdata/first.values b/tools/aconfig/testdata/first.values
index 3c49111..e524404 100644
--- a/tools/aconfig/testdata/first.values
+++ b/tools/aconfig/testdata/first.values
@@ -1,17 +1,17 @@
flag_value {
- namespace: "test"
+ package: "com.android.aconfig.test"
name: "disabled_ro"
state: DISABLED
permission: READ_ONLY
}
flag_value {
- namespace: "test"
+ package: "com.android.aconfig.test"
name: "enabled_ro"
state: DISABLED
permission: READ_WRITE
}
flag_value {
- namespace: "test"
+ package: "com.android.aconfig.test"
name: "enabled_rw"
state: ENABLED
permission: READ_WRITE
diff --git a/tools/aconfig/testdata/second.values b/tools/aconfig/testdata/second.values
index 3fe11ab..aa09cf6 100644
--- a/tools/aconfig/testdata/second.values
+++ b/tools/aconfig/testdata/second.values
@@ -1,5 +1,5 @@
flag_value {
- namespace: "test"
+ package: "com.android.aconfig.test"
name: "enabled_ro"
state: ENABLED
permission: READ_ONLY
diff --git a/tools/aconfig/testdata/test.aconfig b/tools/aconfig/testdata/test.aconfig
index 986a526..d09396a 100644
--- a/tools/aconfig/testdata/test.aconfig
+++ b/tools/aconfig/testdata/test.aconfig
@@ -1,10 +1,11 @@
-namespace: "test"
+package: "com.android.aconfig.test"
# This flag's final value is calculated from:
# - test.aconfig: DISABLED + READ_WRITE (default)
# - first.values: DISABLED + READ_ONLY
flag {
name: "disabled_ro"
+ namespace: "aconfig_test"
description: "This flag is DISABLED + READ_ONLY"
}
@@ -12,6 +13,7 @@
# - test.aconfig: DISABLED + READ_WRITE (default)
flag {
name: "disabled_rw"
+ namespace: "aconfig_test"
description: "This flag is DISABLED + READ_WRITE"
}
@@ -21,6 +23,7 @@
# - second.values: ENABLED + READ_ONLY
flag {
name: "enabled_ro"
+ namespace: "aconfig_test"
description: "This flag is ENABLED + READ_ONLY"
}
@@ -29,5 +32,6 @@
# - first.values: ENABLED + READ_WRITE
flag {
name: "enabled_rw"
+ namespace: "aconfig_test"
description: "This flag is ENABLED + READ_WRITE"
}