Merge changes from topic "aconfig-part-5"
* changes:
aconfig: follow Java conventions for Java file paths
aconfig: separate flag declarations and flag values
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index a3b1fec..9d36a9e 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License
-// This is the schema definition for of Aconfig files. Modifications need to be
-// either backwards compatible, or include updates to all Aconfig files in the
+// This is the schema definition for aconfig files. Modifications need to be
+// either backwards compatible, or include updates to all aconfig files in the
// Android tree.
syntax = "proto2";
@@ -32,40 +32,33 @@
READ_WRITE = 2;
}
-// aconfig input messages: configuration and override data
+// aconfig input messages: flag declarations and values
-message flag_value {
- required flag_state state = 1;
- required flag_permission permission = 2;
- optional uint32 since = 3;
-}
-
-message flag_definition {
+message flag_declaration {
required string name = 1;
required string description = 2;
- repeated flag_value value = 3;
};
-message namespace {
+message flag_declarations {
required string namespace = 1;
- repeated flag_definition flag = 2;
+ repeated flag_declaration flag = 2;
};
-message flag_override {
+message flag_value {
required string namespace = 1;
required string name = 2;
required flag_state state = 3;
required flag_permission permission = 4;
};
-message flag_overrides {
- repeated flag_override flag_override = 1;
+message flag_values {
+ repeated flag_value flag_value = 1;
};
-// aconfig output messages: parsed and verified configuration and override data
+// aconfig output messages: parsed and verified flag declarations and values
message tracepoint {
- // path to config or override file releative to $TOP
+ // path to declaration or value file relative to $TOP
required string source = 1;
required flag_state state = 2;
required flag_permission permission = 3;
diff --git a/tools/aconfig/src/aconfig.rs b/tools/aconfig/src/aconfig.rs
index 8fe82b6..b09648d 100644
--- a/tools/aconfig/src/aconfig.rs
+++ b/tools/aconfig/src/aconfig.rs
@@ -20,9 +20,8 @@
use crate::cache::{Cache, Item, Tracepoint};
use crate::protos::{
- ProtoFlagDefinition, ProtoFlagDefinitionValue, ProtoFlagOverride, ProtoFlagOverrides,
- ProtoFlagPermission, ProtoFlagState, ProtoNamespace, ProtoParsedFlag, ProtoParsedFlags,
- ProtoTracepoint,
+ ProtoFlagDeclaration, ProtoFlagDeclarations, ProtoFlagPermission, ProtoFlagState,
+ ProtoFlagValue, ProtoFlagValues, ProtoParsedFlag, ProtoParsedFlags, ProtoTracepoint,
};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
@@ -80,112 +79,43 @@
}
#[derive(Debug, PartialEq, Eq)]
-pub struct Value {
- state: FlagState,
- permission: Permission,
- since: Option<u32>,
-}
-
-#[allow(dead_code)] // only used in unit tests
-impl Value {
- pub fn new(state: FlagState, permission: Permission, since: u32) -> Value {
- Value { state, permission, since: Some(since) }
- }
-
- pub fn default(state: FlagState, permission: Permission) -> Value {
- Value { state, permission, since: None }
- }
-}
-
-impl TryFrom<ProtoFlagDefinitionValue> for Value {
- type Error = Error;
-
- fn try_from(proto: ProtoFlagDefinitionValue) -> Result<Self, Self::Error> {
- let Some(proto_state) = proto.state else {
- return Err(anyhow!("missing 'state' field"));
- };
- let state = proto_state.try_into()?;
- let Some(proto_permission) = proto.permission else {
- return Err(anyhow!("missing 'permission' field"));
- };
- let permission = proto_permission.try_into()?;
- Ok(Value { state, permission, since: proto.since })
- }
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub struct Flag {
+pub struct FlagDeclaration {
pub name: String,
pub description: String,
-
- // ordered by Value.since; guaranteed to contain at least one item (the default value, with
- // since == None)
- pub values: Vec<Value>,
}
-impl Flag {
+impl FlagDeclaration {
#[allow(dead_code)] // only used in unit tests
- pub fn try_from_text_proto(text_proto: &str) -> Result<Flag> {
- let proto: ProtoFlagDefinition = crate::protos::try_from_text_proto(text_proto)
+ pub fn try_from_text_proto(text_proto: &str) -> Result<FlagDeclaration> {
+ let proto: ProtoFlagDeclaration = crate::protos::try_from_text_proto(text_proto)
.with_context(|| text_proto.to_owned())?;
proto.try_into()
}
-
- pub fn resolve(&self, build_id: u32) -> (FlagState, Permission) {
- let mut state = self.values[0].state;
- let mut permission = self.values[0].permission;
- for candidate in self.values.iter().skip(1) {
- let since = candidate.since.expect("invariant: non-defaults values have Some(since)");
- if since <= build_id {
- state = candidate.state;
- permission = candidate.permission;
- }
- }
- (state, permission)
- }
}
-impl TryFrom<ProtoFlagDefinition> for Flag {
+impl TryFrom<ProtoFlagDeclaration> for FlagDeclaration {
type Error = Error;
- fn try_from(proto: ProtoFlagDefinition) -> Result<Self, Self::Error> {
+ fn try_from(proto: ProtoFlagDeclaration) -> Result<Self, Self::Error> {
let Some(name) = proto.name else {
return Err(anyhow!("missing 'name' field"));
};
let Some(description) = proto.description else {
return Err(anyhow!("missing 'description' field"));
};
- if proto.value.is_empty() {
- return Err(anyhow!("missing 'value' field"));
- }
-
- let mut values: Vec<Value> = vec![];
- for proto_value in proto.value.into_iter() {
- let v: Value = proto_value.try_into()?;
- if values.iter().any(|w| v.since == w.since) {
- let msg = match v.since {
- None => format!("flag {}: multiple default values", name),
- Some(x) => format!("flag {}: multiple values for since={}", name, x),
- };
- return Err(anyhow!(msg));
- }
- values.push(v);
- }
- values.sort_by_key(|v| v.since);
-
- Ok(Flag { name, description, values })
+ Ok(FlagDeclaration { name, description })
}
}
#[derive(Debug, PartialEq, Eq)]
-pub struct Namespace {
+pub struct FlagDeclarations {
pub namespace: String,
- pub flags: Vec<Flag>,
+ pub flags: Vec<FlagDeclaration>,
}
-impl Namespace {
- pub fn try_from_text_proto(text_proto: &str) -> Result<Namespace> {
- let proto: ProtoNamespace = crate::protos::try_from_text_proto(text_proto)
+impl FlagDeclarations {
+ 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 {
return Err(anyhow!("missing 'namespace' field"));
@@ -194,35 +124,35 @@
for proto_flag in proto.flag.into_iter() {
flags.push(proto_flag.try_into()?);
}
- Ok(Namespace { namespace, flags })
+ Ok(FlagDeclarations { namespace, flags })
}
}
#[derive(Debug, PartialEq, Eq)]
-pub struct Override {
+pub struct FlagValue {
pub namespace: String,
pub name: String,
pub state: FlagState,
pub permission: Permission,
}
-impl Override {
+impl FlagValue {
#[allow(dead_code)] // only used in unit tests
- pub fn try_from_text_proto(text_proto: &str) -> Result<Override> {
- let proto: ProtoFlagOverride = crate::protos::try_from_text_proto(text_proto)?;
+ pub fn try_from_text_proto(text_proto: &str) -> Result<FlagValue> {
+ let proto: ProtoFlagValue = crate::protos::try_from_text_proto(text_proto)?;
proto.try_into()
}
- pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Override>> {
- let proto: ProtoFlagOverrides = crate::protos::try_from_text_proto(text_proto)?;
- proto.flag_override.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
+ pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<FlagValue>> {
+ let proto: ProtoFlagValues = crate::protos::try_from_text_proto(text_proto)?;
+ proto.flag_value.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
}
}
-impl TryFrom<ProtoFlagOverride> for Override {
+impl TryFrom<ProtoFlagValue> for FlagValue {
type Error = Error;
- fn try_from(proto: ProtoFlagOverride) -> Result<Self, Self::Error> {
+ fn try_from(proto: ProtoFlagValue) -> Result<Self, Self::Error> {
let Some(namespace) = proto.namespace else {
return Err(anyhow!("missing 'namespace' field"));
};
@@ -237,7 +167,7 @@
return Err(anyhow!("missing 'permission' field"));
};
let permission = proto_permission.try_into()?;
- Ok(Override { namespace, name, state, permission })
+ Ok(FlagValue { namespace, name, state, permission })
}
}
@@ -282,29 +212,16 @@
#[test]
fn test_flag_try_from_text_proto() {
- let expected = Flag {
+ let expected = FlagDeclaration {
name: "1234".to_owned(),
description: "Description of the flag".to_owned(),
- values: vec![
- Value::default(FlagState::Disabled, Permission::ReadOnly),
- Value::new(FlagState::Enabled, Permission::ReadWrite, 8),
- ],
};
let s = r#"
name: "1234"
description: "Description of the flag"
- value {
- state: DISABLED
- permission: READ_ONLY
- }
- value {
- state: ENABLED
- permission: READ_WRITE
- since: 8
- }
"#;
- let actual = Flag::try_from_text_proto(s).unwrap();
+ let actual = FlagDeclaration::try_from_text_proto(s).unwrap();
assert_eq!(expected, actual);
}
@@ -313,52 +230,24 @@
fn test_flag_try_from_text_proto_bad_input() {
let s = r#"
name: "a"
- description: "Description of the flag"
"#;
- let error = Flag::try_from_text_proto(s).unwrap_err();
- assert_eq!(format!("{:?}", error), "missing 'value' field");
-
- let s = r#"
- description: "Description of the flag"
- value {
- state: ENABLED
- permission: READ_ONLY
- }
- "#;
- let error = Flag::try_from_text_proto(s).unwrap_err();
+ let error = FlagDeclaration::try_from_text_proto(s).unwrap_err();
assert!(format!("{:?}", error).contains("Message not initialized"));
let s = r#"
- name: "a"
description: "Description of the flag"
- value {
- state: ENABLED
- permission: READ_ONLY
- }
- value {
- state: ENABLED
- permission: READ_ONLY
- }
"#;
- let error = Flag::try_from_text_proto(s).unwrap_err();
- assert_eq!(format!("{:?}", error), "flag a: multiple default values");
+ let error = FlagDeclaration::try_from_text_proto(s).unwrap_err();
+ assert!(format!("{:?}", error).contains("Message not initialized"));
}
#[test]
fn test_namespace_try_from_text_proto() {
- let expected = Namespace {
+ let expected = FlagDeclarations {
namespace: "ns".to_owned(),
flags: vec![
- Flag {
- name: "a".to_owned(),
- description: "A".to_owned(),
- values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
- },
- Flag {
- name: "b".to_owned(),
- description: "B".to_owned(),
- values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
- },
+ FlagDeclaration { name: "a".to_owned(), description: "A".to_owned() },
+ FlagDeclaration { name: "b".to_owned(), description: "B".to_owned() },
],
};
@@ -367,28 +256,20 @@
flag {
name: "a"
description: "A"
- value {
- state: ENABLED
- permission: READ_ONLY
- }
}
flag {
name: "b"
description: "B"
- value {
- state: DISABLED
- permission: READ_WRITE
- }
}
"#;
- let actual = Namespace::try_from_text_proto(s).unwrap();
+ let actual = FlagDeclarations::try_from_text_proto(s).unwrap();
assert_eq!(expected, actual);
}
#[test]
- fn test_override_try_from_text_proto_list() {
- let expected = Override {
+ fn test_flag_declaration_try_from_text_proto_list() {
+ let expected = FlagValue {
namespace: "ns".to_owned(),
name: "1234".to_owned(),
state: FlagState::Enabled,
@@ -401,32 +282,8 @@
state: ENABLED
permission: READ_ONLY
"#;
- let actual = Override::try_from_text_proto(s).unwrap();
+ let actual = FlagValue::try_from_text_proto(s).unwrap();
assert_eq!(expected, actual);
}
-
- #[test]
- fn test_flag_resolve() {
- let flag = Flag {
- name: "a".to_owned(),
- description: "A".to_owned(),
- values: vec![
- Value::default(FlagState::Disabled, Permission::ReadOnly),
- Value::new(FlagState::Disabled, Permission::ReadWrite, 10),
- Value::new(FlagState::Enabled, Permission::ReadOnly, 20),
- Value::new(FlagState::Enabled, Permission::ReadWrite, 30),
- ],
- };
- assert_eq!((FlagState::Disabled, Permission::ReadOnly), flag.resolve(0));
- assert_eq!((FlagState::Disabled, Permission::ReadOnly), flag.resolve(9));
- assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(10));
- assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(11));
- assert_eq!((FlagState::Disabled, Permission::ReadWrite), flag.resolve(19));
- assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(20));
- assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(21));
- assert_eq!((FlagState::Enabled, Permission::ReadOnly), flag.resolve(29));
- assert_eq!((FlagState::Enabled, Permission::ReadWrite), flag.resolve(30));
- assert_eq!((FlagState::Enabled, Permission::ReadWrite), flag.resolve(10_000));
- }
}
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index 4b46c42..3ecadc9 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, bail, ensure, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Write};
-use crate::aconfig::{Flag, FlagState, Override, Permission};
+use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
use crate::commands::Source;
+const DEFAULT_FLAG_STATE: FlagState = FlagState::Disabled;
+const DEFAULT_FLAG_PERMISSION: Permission = Permission::ReadWrite;
+
#[derive(Serialize, Deserialize, Debug)]
pub struct Tracepoint {
pub source: Source,
@@ -44,14 +47,13 @@
#[derive(Serialize, Deserialize, Debug)]
pub struct Cache {
- build_id: u32,
namespace: String,
items: Vec<Item>,
}
impl Cache {
- pub fn new(build_id: u32, namespace: String) -> Cache {
- Cache { build_id, namespace, items: vec![] }
+ pub fn new(namespace: String) -> Cache {
+ Cache { namespace, items: vec![] }
}
pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
@@ -62,40 +64,51 @@
serde_json::to_writer(writer, self).map_err(|e| e.into())
}
- pub fn add_flag(&mut self, source: Source, flag: Flag) -> Result<()> {
- if self.items.iter().any(|item| item.name == flag.name) {
+ pub fn add_flag_declaration(
+ &mut self,
+ source: Source,
+ declaration: FlagDeclaration,
+ ) -> Result<()> {
+ if self.items.iter().any(|item| item.name == declaration.name) {
return Err(anyhow!(
- "failed to add flag {} from {}: flag already defined",
- flag.name,
+ "failed to declare flag {} from {}: flag already declared",
+ declaration.name,
source,
));
}
- let (state, permission) = flag.resolve(self.build_id);
self.items.push(Item {
namespace: self.namespace.clone(),
- name: flag.name.clone(),
- description: flag.description,
- state,
- permission,
- trace: vec![Tracepoint { source, state, permission }],
+ name: declaration.name.clone(),
+ description: declaration.description,
+ state: DEFAULT_FLAG_STATE,
+ permission: DEFAULT_FLAG_PERMISSION,
+ trace: vec![Tracepoint {
+ source,
+ state: DEFAULT_FLAG_STATE,
+ permission: DEFAULT_FLAG_PERMISSION,
+ }],
});
Ok(())
}
- pub fn add_override(&mut self, source: Source, override_: Override) -> Result<()> {
- if override_.namespace != self.namespace {
- // TODO: print warning?
- return Ok(());
- }
- let Some(existing_item) = self.items.iter_mut().find(|item| item.name == override_.name) else {
- return Err(anyhow!("failed to override flag {}: unknown flag", override_.name));
+ pub fn add_flag_value(&mut self, source: Source, value: FlagValue) -> Result<()> {
+ ensure!(
+ value.namespace == self.namespace,
+ "failed to set values for flag {}/{} from {}: expected namespace {}",
+ value.namespace,
+ value.name,
+ source,
+ self.namespace
+ );
+ let Some(existing_item) = self.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);
};
- existing_item.state = override_.state;
- existing_item.permission = override_.permission;
+ existing_item.state = value.state;
+ existing_item.permission = value.permission;
existing_item.trace.push(Tracepoint {
source,
- state: override_.state,
- permission: override_.permission,
+ state: value.state,
+ permission: value.permission,
});
Ok(())
}
@@ -112,49 +125,41 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::aconfig::{FlagState, Permission, Value};
+ use crate::aconfig::{FlagState, Permission};
#[test]
- fn test_add_flag() {
- let mut cache = Cache::new(1, "ns".to_string());
+ fn test_add_flag_declaration() {
+ let mut cache = Cache::new("ns".to_string());
cache
- .add_flag(
+ .add_flag_declaration(
Source::File("first.txt".to_string()),
- Flag {
- name: "foo".to_string(),
- description: "desc".to_string(),
- values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
- },
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
)
.unwrap();
let error = cache
- .add_flag(
+ .add_flag_declaration(
Source::File("second.txt".to_string()),
- Flag {
- name: "foo".to_string(),
- description: "desc".to_string(),
- values: vec![Value::default(FlagState::Disabled, Permission::ReadOnly)],
- },
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
)
.unwrap_err();
assert_eq!(
&format!("{:?}", error),
- "failed to add flag foo from second.txt: flag already defined"
+ "failed to declare flag foo from second.txt: flag already declared"
);
}
#[test]
- fn test_add_override() {
+ fn test_add_flag_value() {
fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
let item = cache.iter().find(|&item| item.name == name).unwrap();
item.state == expected.0 && item.permission == expected.1
}
- let mut cache = Cache::new(1, "ns".to_string());
+ let mut cache = Cache::new("ns".to_string());
let error = cache
- .add_override(
+ .add_flag_value(
Source::Memory,
- Override {
+ FlagValue {
namespace: "ns".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
@@ -162,39 +167,36 @@
},
)
.unwrap_err();
- assert_eq!(&format!("{:?}", error), "failed to override flag foo: unknown flag");
+ assert_eq!(
+ &format!("{:?}", error),
+ "failed to set values for flag ns/foo from <memory>: flag not declared"
+ );
cache
- .add_flag(
+ .add_flag_declaration(
Source::File("first.txt".to_string()),
- Flag {
- name: "foo".to_string(),
- description: "desc".to_string(),
- values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
- },
+ FlagDeclaration { name: "foo".to_string(), description: "desc".to_string() },
)
.unwrap();
- dbg!(&cache);
- assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadOnly)));
+ assert!(check(&cache, "foo", (DEFAULT_FLAG_STATE, DEFAULT_FLAG_PERMISSION)));
cache
- .add_override(
+ .add_flag_value(
Source::Memory,
- Override {
+ FlagValue {
namespace: "ns".to_string(),
name: "foo".to_string(),
state: FlagState::Disabled,
- permission: Permission::ReadWrite,
+ permission: Permission::ReadOnly,
},
)
.unwrap();
- dbg!(&cache);
- assert!(check(&cache, "foo", (FlagState::Disabled, Permission::ReadWrite)));
+ assert!(check(&cache, "foo", (FlagState::Disabled, Permission::ReadOnly)));
cache
- .add_override(
+ .add_flag_value(
Source::Memory,
- Override {
+ FlagValue {
namespace: "ns".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
@@ -205,17 +207,18 @@
assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
// different namespace -> no-op
- cache
- .add_override(
+ let error = cache
+ .add_flag_value(
Source::Memory,
- Override {
+ FlagValue {
namespace: "some-other-namespace".to_string(),
name: "foo".to_string(),
state: FlagState::Enabled,
permission: Permission::ReadOnly,
},
)
- .unwrap();
+ .unwrap_err();
+ assert_eq!(&format!("{:?}", error), "failed to set values for flag some-other-namespace/foo from <memory>: expected namespace ns");
assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
}
}
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 9d52cce..bbf1272 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -20,13 +20,9 @@
use crate::aconfig::{FlagState, Permission};
use crate::cache::{Cache, Item};
+use crate::commands::OutputFile;
-pub struct GeneratedFile {
- pub file_content: String,
- pub file_name: String,
-}
-
-pub fn generate_java_code(cache: &Cache) -> Result<GeneratedFile> {
+pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
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(
@@ -35,8 +31,9 @@
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) })
+ let contents = template.render("java_code_gen", &context)?;
+ let path = ["com", "android", "internal", "aconfig", &(namespace + ".java")].iter().collect();
+ Ok(OutputFile { contents: contents.into(), path })
}
#[derive(Serialize)]
@@ -87,56 +84,67 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::aconfig::{Flag, Value};
+ use crate::aconfig::{FlagDeclaration, FlagValue};
use crate::commands::Source;
#[test]
fn test_generate_java_code() {
let namespace = "TeSTFlaG";
- let mut cache = Cache::new(1, namespace.to_string());
+ let mut cache = Cache::new(namespace.to_string());
cache
- .add_flag(
+ .add_flag_declaration(
Source::File("test.txt".to_string()),
- Flag {
+ FlagDeclaration {
name: "test".to_string(),
description: "buildtime enable".to_string(),
- values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
},
)
.unwrap();
cache
- .add_flag(
+ .add_flag_declaration(
Source::File("test2.txt".to_string()),
- Flag {
+ FlagDeclaration {
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;
+ cache
+ .add_flag_value(
+ Source::Memory,
+ FlagValue {
+ namespace: namespace.to_string(),
+ name: "test".to_string(),
+ state: FlagState::Disabled,
+ permission: Permission::ReadOnly,
+ },
+ )
+ .unwrap();
+ let expect_content = r#"package com.android.internal.aconfig;
import android.provider.DeviceConfig;
public final class Testflag {
public static boolean test() {
- return true;
+ return false;
}
public static boolean test2() {
return DeviceConfig.getBoolean(
- \"Testflag\",
- \"test2__test2\",
+ "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(' ', ""));
+ "#;
+ let file = generate_java_code(&cache).unwrap();
+ assert_eq!("com/android/internal/aconfig/Testflag.java", file.path.to_str().unwrap());
+ assert_eq!(
+ expect_content.replace(' ', ""),
+ String::from_utf8(file.contents).unwrap().replace(' ', "")
+ );
}
}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 1487e72..475d9b8 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -20,10 +20,11 @@
use serde::{Deserialize, Serialize};
use std::fmt;
use std::io::Read;
+use std::path::PathBuf;
-use crate::aconfig::{Namespace, Override};
+use crate::aconfig::{FlagDeclarations, FlagValue};
use crate::cache::Cache;
-use crate::codegen_java::{generate_java_code, GeneratedFile};
+use crate::codegen_java::generate_java_code;
use crate::protos::ProtoParsedFlags;
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -47,45 +48,50 @@
pub reader: Box<dyn Read>,
}
-pub fn create_cache(
- build_id: u32,
- namespace: &str,
- aconfigs: Vec<Input>,
- overrides: Vec<Input>,
-) -> Result<Cache> {
- let mut cache = Cache::new(build_id, namespace.to_owned());
+pub struct OutputFile {
+ pub path: PathBuf, // relative to some root directory only main knows about
+ pub contents: Vec<u8>,
+}
- for mut input in aconfigs {
+pub fn create_cache(
+ namespace: &str,
+ declarations: Vec<Input>,
+ values: Vec<Input>,
+) -> Result<Cache> {
+ let mut cache = Cache::new(namespace.to_owned());
+
+ for mut input in declarations {
let mut contents = String::new();
input.reader.read_to_string(&mut contents)?;
- let ns = Namespace::try_from_text_proto(&contents)
+ let dec_list = FlagDeclarations::try_from_text_proto(&contents)
.with_context(|| format!("Failed to parse {}", input.source))?;
ensure!(
- namespace == ns.namespace,
+ namespace == dec_list.namespace,
"Failed to parse {}: expected namespace {}, got {}",
input.source,
namespace,
- ns.namespace
+ dec_list.namespace
);
- for flag in ns.flags.into_iter() {
- cache.add_flag(input.source.clone(), flag)?;
+ for d in dec_list.flags.into_iter() {
+ cache.add_flag_declaration(input.source.clone(), d)?;
}
}
- for mut input in overrides {
+ for mut input in values {
let mut contents = String::new();
input.reader.read_to_string(&mut contents)?;
- let overrides = Override::try_from_text_proto_list(&contents)
+ let values_list = FlagValue::try_from_text_proto_list(&contents)
.with_context(|| format!("Failed to parse {}", input.source))?;
- for override_ in overrides {
- cache.add_override(input.source.clone(), override_)?;
+ for v in values_list {
+ // TODO: warn about flag values that do not take effect?
+ let _ = cache.add_flag_value(input.source.clone(), v);
}
}
Ok(cache)
}
-pub fn generate_code(cache: &Cache) -> Result<GeneratedFile> {
+pub fn generate_code(cache: &Cache) -> Result<OutputFile> {
generate_java_code(cache)
}
@@ -132,31 +138,23 @@
flag {
name: "a"
description: "Description of a"
- value {
- state: ENABLED
- permission: READ_WRITE
- }
}
flag {
name: "b"
description: "Description of b"
- value {
- state: ENABLED
- permission: READ_ONLY
- }
}
"#;
- let aconfigs = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
+ let declarations = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
let o = r#"
- flag_override {
+ flag_value {
namespace: "ns"
name: "a"
state: DISABLED
permission: READ_ONLY
}
"#;
- let overrides = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
- create_cache(1, "ns", aconfigs, overrides).unwrap()
+ let values = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
+ create_cache("ns", declarations, values).unwrap()
}
#[test]
@@ -194,12 +192,12 @@
assert_eq!(item.namespace(), "ns");
assert_eq!(item.name(), "b");
assert_eq!(item.description(), "Description of b");
- assert_eq!(item.state(), ProtoFlagState::ENABLED);
- assert_eq!(item.permission(), ProtoFlagPermission::READ_ONLY);
+ assert_eq!(item.state(), ProtoFlagState::DISABLED);
+ assert_eq!(item.permission(), ProtoFlagPermission::READ_WRITE);
let mut tp = ProtoTracepoint::new();
tp.set_source("<memory>".to_string());
- tp.set_state(ProtoFlagState::ENABLED);
- tp.set_permission(ProtoFlagPermission::READ_ONLY);
+ tp.set_state(ProtoFlagState::DISABLED);
+ tp.set_permission(ProtoFlagPermission::READ_WRITE);
assert_eq!(item.trace, vec![tp]);
}
}
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index f29186a..513e313 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -16,11 +16,12 @@
//! `aconfig` is a build time tool to manage build time configurations, such as feature flags.
-use anyhow::Result;
+use anyhow::{anyhow, ensure, Result};
use clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command};
use std::fs;
use std::io;
use std::io::Write;
+use std::path::{Path, PathBuf};
mod aconfig;
mod cache;
@@ -29,22 +30,16 @@
mod protos;
use crate::cache::Cache;
-use commands::{Input, Source};
+use commands::{Input, OutputFile, Source};
fn cli() -> Command {
Command::new("aconfig")
.subcommand_required(true)
.subcommand(
Command::new("create-cache")
- .arg(
- Arg::new("build-id")
- .long("build-id")
- .value_parser(clap::value_parser!(u32))
- .required(true),
- )
.arg(Arg::new("namespace").long("namespace").required(true))
- .arg(Arg::new("aconfig").long("aconfig").action(ArgAction::Append))
- .arg(Arg::new("override").long("override").action(ArgAction::Append))
+ .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)),
)
.subcommand(
@@ -74,15 +69,30 @@
Ok(opened_files)
}
+fn write_output_file_realtive_to_dir(root: &Path, output_file: &OutputFile) -> Result<()> {
+ ensure!(
+ root.is_dir(),
+ "output directory {} does not exist or is not a directory",
+ root.display()
+ );
+ let path = root.join(output_file.path.clone());
+ let parent = path
+ .parent()
+ .ok_or(anyhow!("unable to locate parent of output file {}", path.display()))?;
+ fs::create_dir_all(parent)?;
+ let mut file = fs::File::create(path)?;
+ file.write_all(&output_file.contents)?;
+ Ok(())
+}
+
fn main() -> Result<()> {
let matches = cli().get_matches();
match matches.subcommand() {
Some(("create-cache", sub_matches)) => {
- let build_id = *sub_matches.get_one::<u32>("build-id").unwrap();
let namespace = sub_matches.get_one::<String>("namespace").unwrap();
- let aconfigs = open_zero_or_more_files(sub_matches, "aconfig")?;
- let overrides = open_zero_or_more_files(sub_matches, "override")?;
- let cache = commands::create_cache(build_id, namespace, aconfigs, overrides)?;
+ 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 path = sub_matches.get_one::<String>("cache").unwrap();
let file = fs::File::create(path)?;
cache.write_to_writer(file)?;
@@ -91,12 +101,9 @@
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 dir = PathBuf::from(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,
- )?;
+ write_output_file_realtive_to_dir(&dir, &generated_file)?;
}
Some(("dump", sub_matches)) => {
let path = sub_matches.get_one::<String>("cache").unwrap();
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
index 5965a09..cb75692 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/src/protos.rs
@@ -28,19 +28,16 @@
// ---- When building with the Android tool-chain ----
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Namespace as ProtoNamespace;
+pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Flag_value as ProtoFlagDefinitionValue;
+pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Flag_definition as ProtoFlagDefinition;
+pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Flag_overrides as ProtoFlagOverrides;
-
-#[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Flag_override as ProtoFlagOverride;
+pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
#[cfg(not(feature = "cargo"))]
pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
@@ -62,19 +59,16 @@
include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
#[cfg(feature = "cargo")]
-pub use aconfig::Namespace as ProtoNamespace;
+pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
#[cfg(feature = "cargo")]
-pub use aconfig::Flag_value as ProtoFlagDefinitionValue;
+pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
#[cfg(feature = "cargo")]
-pub use aconfig::Flag_definition as ProtoFlagDefinition;
+pub use aconfig::Flag_value as ProtoFlagValue;
#[cfg(feature = "cargo")]
-pub use aconfig::Flag_overrides as ProtoFlagOverrides;
-
-#[cfg(feature = "cargo")]
-pub use aconfig::Flag_override as ProtoFlagOverride;
+pub use aconfig::Flag_values as ProtoFlagValues;
#[cfg(feature = "cargo")]
pub use aconfig::Flag_permission as ProtoFlagPermission;
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
index 3854579..ebcd607 100644
--- a/tools/aconfig/templates/java.template
+++ b/tools/aconfig/templates/java.template
@@ -1,4 +1,4 @@
-package com.android.aconfig;
+package com.android.internal.aconfig;
{{ if readwrite }}
import android.provider.DeviceConfig;
{{ endif }}