Merge "Raise go.mod version of build/make/tools/rbcrun" into main
diff --git a/core/Makefile b/core/Makefile
index 7d7457e..4a1cda1 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -5626,7 +5626,12 @@
FASTBOOT_INFO_VERSION = 1
INSTALLED_FASTBOOT_INFO_TARGET := $(PRODUCT_OUT)/fastboot-info.txt
-
+ifdef TARGET_BOARD_FASTBOOT_INFO_FILE
+$(INSTALLED_FASTBOOT_INFO_TARGET): $(TARGET_BOARD_FASTBOOT_INFO_FILE)
+ rm -f $@
+ $(call pretty,"Target fastboot-info.txt: $@")
+ $(hide) cp $< $@
+else
$(INSTALLED_FASTBOOT_INFO_TARGET):
rm -f $@
$(call pretty,"Target fastboot-info.txt: $@")
@@ -5685,6 +5690,7 @@
ifeq ($(BOARD_USES_METADATA_PARTITION),true)
$(hide) echo "if-wipe erase metadata" >> $@
endif
+endif
# -----------------------------------------------------------------
# misc_info.txt
diff --git a/tools/aconfig/src/codegen/cpp.rs b/tools/aconfig/src/codegen/cpp.rs
index 000581b..ada022a 100644
--- a/tools/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/src/codegen/cpp.rs
@@ -23,17 +23,17 @@
use crate::commands::{CodegenMode, OutputFile};
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
-pub fn generate_cpp_code<'a, I>(
+pub fn generate_cpp_code<I>(
package: &str,
parsed_flags_iter: I,
codegen_mode: CodegenMode,
) -> Result<Vec<OutputFile>>
where
- I: Iterator<Item = &'a ProtoParsedFlag>,
+ I: Iterator<Item = ProtoParsedFlag>,
{
let mut readwrite_count = 0;
let class_elements: Vec<ClassElement> = parsed_flags_iter
- .map(|pf| create_class_element(package, pf, &mut readwrite_count))
+ .map(|pf| create_class_element(package, &pf, &mut readwrite_count))
.collect();
let readwrite = readwrite_count > 0;
let has_fixed_read_only = class_elements.iter().any(|item| item.is_fixed_read_only);
@@ -734,9 +734,12 @@
fn test_generate_cpp_code(mode: CodegenMode) {
let parsed_flags = crate::test::parse_test_flags();
- let generated =
- generate_cpp_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter(), mode)
- .unwrap();
+ let generated = generate_cpp_code(
+ crate::test::TEST_PACKAGE,
+ parsed_flags.parsed_flag.into_iter(),
+ mode,
+ )
+ .unwrap();
let mut generated_files_map = HashMap::new();
for file in generated {
generated_files_map.insert(
diff --git a/tools/aconfig/src/codegen/java.rs b/tools/aconfig/src/codegen/java.rs
index ae3f274..1d5dabf 100644
--- a/tools/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/src/codegen/java.rs
@@ -24,16 +24,16 @@
use crate::commands::{CodegenMode, OutputFile};
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
-pub fn generate_java_code<'a, I>(
+pub fn generate_java_code<I>(
package: &str,
parsed_flags_iter: I,
codegen_mode: CodegenMode,
) -> Result<Vec<OutputFile>>
where
- I: Iterator<Item = &'a ProtoParsedFlag>,
+ I: Iterator<Item = ProtoParsedFlag>,
{
let flag_elements: Vec<FlagElement> =
- parsed_flags_iter.map(|pf| create_flag_element(package, pf)).collect();
+ parsed_flags_iter.map(|pf| create_flag_element(package, &pf)).collect();
let exported_flag_elements: Vec<FlagElement> =
flag_elements.iter().filter(|elem| elem.exported).cloned().collect();
let namespace_flags = gen_flags_by_namespace(&flag_elements);
@@ -361,7 +361,7 @@
let parsed_flags = crate::test::parse_test_flags();
let generated_files = generate_java_code(
crate::test::TEST_PACKAGE,
- parsed_flags.parsed_flag.iter(),
+ parsed_flags.parsed_flag.into_iter(),
CodegenMode::Production,
)
.unwrap();
@@ -514,7 +514,7 @@
let parsed_flags = crate::test::parse_test_flags();
let generated_files = generate_java_code(
crate::test::TEST_PACKAGE,
- parsed_flags.parsed_flag.iter(),
+ parsed_flags.parsed_flag.into_iter(),
CodegenMode::Exported,
)
.unwrap();
@@ -705,7 +705,7 @@
let parsed_flags = crate::test::parse_test_flags();
let generated_files = generate_java_code(
crate::test::TEST_PACKAGE,
- parsed_flags.parsed_flag.iter(),
+ parsed_flags.parsed_flag.into_iter(),
CodegenMode::Test,
)
.unwrap();
diff --git a/tools/aconfig/src/codegen/rust.rs b/tools/aconfig/src/codegen/rust.rs
index 04be93b..7aafddb 100644
--- a/tools/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/src/codegen/rust.rs
@@ -22,16 +22,16 @@
use crate::commands::{CodegenMode, OutputFile};
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
-pub fn generate_rust_code<'a, I>(
+pub fn generate_rust_code<I>(
package: &str,
parsed_flags_iter: I,
codegen_mode: CodegenMode,
) -> Result<OutputFile>
where
- I: Iterator<Item = &'a ProtoParsedFlag>,
+ I: Iterator<Item = ProtoParsedFlag>,
{
let template_flags: Vec<TemplateParsedFlag> =
- parsed_flags_iter.map(|pf| TemplateParsedFlag::new(package, pf)).collect();
+ parsed_flags_iter.map(|pf| TemplateParsedFlag::new(package, &pf)).collect();
let has_readwrite = template_flags.iter().any(|item| item.readwrite);
let context = TemplateContext {
package: package.to_string(),
@@ -456,9 +456,12 @@
fn test_generate_rust_code(mode: CodegenMode) {
let parsed_flags = crate::test::parse_test_flags();
- let generated =
- generate_rust_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter(), mode)
- .unwrap();
+ let generated = generate_rust_code(
+ crate::test::TEST_PACKAGE,
+ parsed_flags.parsed_flag.into_iter(),
+ mode,
+ )
+ .unwrap();
assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
assert_eq!(
None,
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index e437c02..87905fd 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -23,12 +23,12 @@
use crate::codegen::cpp::generate_cpp_code;
use crate::codegen::java::generate_java_code;
use crate::codegen::rust::generate_rust_code;
-use crate::storage::generate_storage_files;
-
+use crate::dump::{DumpFormat, DumpPredicate};
use crate::protos::{
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
ProtoParsedFlags, ProtoTracepoint,
};
+use crate::storage::generate_storage_files;
pub struct Input {
pub source: String,
@@ -201,7 +201,8 @@
let Some(package) = find_unique_package(&filtered_parsed_flags) else {
bail!("no parsed flags, or the parsed flags use different packages");
};
- generate_java_code(package, filtered_parsed_flags.iter(), codegen_mode)
+ let package = package.to_string();
+ generate_java_code(&package, filtered_parsed_flags.into_iter(), codegen_mode)
}
pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
@@ -210,7 +211,8 @@
let Some(package) = find_unique_package(&filtered_parsed_flags) else {
bail!("no parsed flags, or the parsed flags use different packages");
};
- generate_cpp_code(package, filtered_parsed_flags.iter(), codegen_mode)
+ let package = package.to_string();
+ generate_cpp_code(&package, filtered_parsed_flags.into_iter(), codegen_mode)
}
pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {
@@ -219,7 +221,8 @@
let Some(package) = find_unique_package(&filtered_parsed_flags) else {
bail!("no parsed flags, or the parsed flags use different packages");
};
- generate_rust_code(package, filtered_parsed_flags.iter(), codegen_mode)
+ let package = package.to_string();
+ generate_rust_code(&package, filtered_parsed_flags.into_iter(), codegen_mode)
}
pub fn create_storage(caches: Vec<Input>, container: &str) -> Result<Vec<OutputFile>> {
@@ -276,73 +279,28 @@
Ok(output)
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
-pub enum DumpFormat {
- Text,
- Verbose,
- Protobuf,
- Textproto,
- Bool,
-}
-
pub fn dump_parsed_flags(
mut input: Vec<Input>,
format: DumpFormat,
+ filters: &[&str],
dedup: bool,
) -> Result<Vec<u8>> {
let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> =
input.iter_mut().map(|i| i.try_parse_flags()).collect();
let parsed_flags: ProtoParsedFlags =
crate::protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;
-
- let mut output = Vec::new();
- match format {
- DumpFormat::Text => {
- for parsed_flag in parsed_flags.parsed_flag.into_iter() {
- let line = format!(
- "{} [{}]: {:?} + {:?}\n",
- parsed_flag.fully_qualified_name(),
- parsed_flag.container(),
- parsed_flag.permission(),
- parsed_flag.state()
- );
- output.extend_from_slice(line.as_bytes());
- }
- }
- DumpFormat::Verbose => {
- for parsed_flag in parsed_flags.parsed_flag.into_iter() {
- let sources: Vec<_> =
- parsed_flag.trace.iter().map(|tracepoint| tracepoint.source()).collect();
- let line = format!(
- "{} [{}]: {:?} + {:?} ({})\n",
- parsed_flag.fully_qualified_name(),
- parsed_flag.container(),
- parsed_flag.permission(),
- parsed_flag.state(),
- sources.join(", ")
- );
- output.extend_from_slice(line.as_bytes());
- }
- }
- DumpFormat::Protobuf => {
- parsed_flags.write_to_vec(&mut output)?;
- }
- DumpFormat::Textproto => {
- let s = protobuf::text_format::print_to_string_pretty(&parsed_flags);
- output.extend_from_slice(s.as_bytes());
- }
- DumpFormat::Bool => {
- for parsed_flag in parsed_flags.parsed_flag.into_iter() {
- let line = format!(
- "{}={:?}\n",
- parsed_flag.fully_qualified_name(),
- parsed_flag.state() == ProtoFlagState::ENABLED
- );
- output.extend_from_slice(line.as_bytes());
- }
- }
- }
- Ok(output)
+ let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() {
+ vec![Box::new(|_| true)]
+ } else {
+ filters
+ .iter()
+ .map(|f| crate::dump::create_filter_predicate(f))
+ .collect::<Result<Vec<_>>>()?
+ };
+ crate::dump::dump_parsed_flags(
+ parsed_flags.parsed_flag.into_iter().filter(|flag| filters.iter().any(|p| p(flag))),
+ format,
+ )
}
fn find_unique_package(parsed_flags: &[ProtoParsedFlag]) -> Option<&str> {
@@ -619,43 +577,25 @@
}
#[test]
- fn test_dump_text_format() {
+ fn test_dump() {
let input = parse_test_flags_as_input();
- let bytes = dump_parsed_flags(vec![input], DumpFormat::Text, false).unwrap();
- let text = std::str::from_utf8(&bytes).unwrap();
- assert!(
- text.contains("com.android.aconfig.test.disabled_ro [system]: READ_ONLY + DISABLED")
- );
- }
-
- #[test]
- fn test_dump_protobuf_format() {
- let expected = protobuf::text_format::parse_from_str::<ProtoParsedFlags>(
- crate::test::TEST_FLAGS_TEXTPROTO,
+ let bytes = dump_parsed_flags(
+ vec![input],
+ DumpFormat::Custom("{fully_qualified_name}".to_string()),
+ &[],
+ false,
)
- .unwrap()
- .write_to_bytes()
.unwrap();
-
- let input = parse_test_flags_as_input();
- let actual = dump_parsed_flags(vec![input], DumpFormat::Protobuf, false).unwrap();
-
- assert_eq!(expected, actual);
- }
-
- #[test]
- fn test_dump_textproto_format() {
- let input = parse_test_flags_as_input();
- let bytes = dump_parsed_flags(vec![input], DumpFormat::Textproto, false).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
- assert_eq!(crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim());
+ assert!(text.contains("com.android.aconfig.test.disabled_ro"));
}
#[test]
fn test_dump_textproto_format_dedup() {
let input = parse_test_flags_as_input();
let input2 = parse_test_flags_as_input();
- let bytes = dump_parsed_flags(vec![input, input2], DumpFormat::Textproto, true).unwrap();
+ let bytes =
+ dump_parsed_flags(vec![input, input2], DumpFormat::Textproto, &[], true).unwrap();
let text = std::str::from_utf8(&bytes).unwrap();
assert_eq!(crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim());
}
diff --git a/tools/aconfig/src/dump.rs b/tools/aconfig/src/dump.rs
new file mode 100644
index 0000000..f2b3687
--- /dev/null
+++ b/tools/aconfig/src/dump.rs
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::protos::{
+ ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint,
+};
+use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
+use anyhow::{anyhow, bail, Context, Result};
+use protobuf::Message;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum DumpFormat {
+ Protobuf,
+ Textproto,
+ Custom(String),
+}
+
+impl TryFrom<&str> for DumpFormat {
+ type Error = anyhow::Error;
+
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
+ match value {
+ // protobuf formats
+ "protobuf" => Ok(Self::Protobuf),
+ "textproto" => Ok(Self::Textproto),
+
+ // old formats now implemented as aliases to custom format
+ "text" => Ok(Self::Custom(
+ "{fully_qualified_name} [{container}]: {permission} + {state}".to_owned(),
+ )),
+ "verbose" => Ok(Self::Custom(
+ "{fully_qualified_name} [{container}]: {permission} + {state} ({trace:paths})"
+ .to_owned(),
+ )),
+ "bool" => Ok(Self::Custom("{fully_qualified_name}={state:bool}".to_owned())),
+
+ // custom format
+ _ => Ok(Self::Custom(value.to_owned())),
+ }
+ }
+}
+
+pub fn dump_parsed_flags<I>(parsed_flags_iter: I, format: DumpFormat) -> Result<Vec<u8>>
+where
+ I: Iterator<Item = ProtoParsedFlag>,
+{
+ let mut output = Vec::new();
+ match format {
+ DumpFormat::Protobuf => {
+ let parsed_flags =
+ ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() };
+ parsed_flags.write_to_vec(&mut output)?;
+ }
+ DumpFormat::Textproto => {
+ let parsed_flags =
+ ProtoParsedFlags { parsed_flag: parsed_flags_iter.collect(), ..Default::default() };
+ let s = protobuf::text_format::print_to_string_pretty(&parsed_flags);
+ output.extend_from_slice(s.as_bytes());
+ }
+ DumpFormat::Custom(format) => {
+ for flag in parsed_flags_iter {
+ dump_custom_format(&flag, &format, &mut output);
+ }
+ }
+ }
+ Ok(output)
+}
+
+fn dump_custom_format(flag: &ProtoParsedFlag, format: &str, output: &mut Vec<u8>) {
+ fn format_trace(trace: &[ProtoTracepoint]) -> String {
+ trace
+ .iter()
+ .map(|tracepoint| {
+ format!(
+ "{}: {:?} + {:?}",
+ tracepoint.source(),
+ tracepoint.permission(),
+ tracepoint.state()
+ )
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+
+ fn format_trace_paths(trace: &[ProtoTracepoint]) -> String {
+ trace.iter().map(|tracepoint| tracepoint.source()).collect::<Vec<_>>().join(", ")
+ }
+
+ fn format_metadata(metadata: &ProtoFlagMetadata) -> String {
+ format!("{:?}", metadata.purpose())
+ }
+
+ let mut str = format
+ // ProtoParsedFlag fields
+ .replace("{package}", flag.package())
+ .replace("{name}", flag.name())
+ .replace("{namespace}", flag.namespace())
+ .replace("{description}", flag.description())
+ .replace("{bug}", &flag.bug.join(", "))
+ .replace("{state}", &format!("{:?}", flag.state()))
+ .replace("{state:bool}", &format!("{}", flag.state() == ProtoFlagState::ENABLED))
+ .replace("{permission}", &format!("{:?}", flag.permission()))
+ .replace("{trace}", &format_trace(&flag.trace))
+ .replace("{trace:paths}", &format_trace_paths(&flag.trace))
+ .replace("{is_fixed_read_only}", &format!("{}", flag.is_fixed_read_only()))
+ .replace("{is_exported}", &format!("{}", flag.is_exported()))
+ .replace("{container}", flag.container())
+ .replace("{metadata}", &format_metadata(&flag.metadata))
+ // ParsedFlagExt functions
+ .replace("{fully_qualified_name}", &flag.fully_qualified_name());
+ str.push('\n');
+ output.extend_from_slice(str.as_bytes());
+}
+
+pub type DumpPredicate = dyn Fn(&ProtoParsedFlag) -> bool;
+
+pub fn create_filter_predicate(filter: &str) -> Result<Box<DumpPredicate>> {
+ let predicates = filter
+ .split('+')
+ .map(|sub_filter| create_filter_predicate_single(sub_filter))
+ .collect::<Result<Vec<_>>>()?;
+ Ok(Box::new(move |flag| predicates.iter().all(|p| p(flag))))
+}
+
+fn create_filter_predicate_single(filter: &str) -> Result<Box<DumpPredicate>> {
+ fn enum_from_str<T>(expected: &[T], s: &str) -> Result<T>
+ where
+ T: std::fmt::Debug + Copy,
+ {
+ for candidate in expected.iter() {
+ if s == format!("{:?}", candidate) {
+ return Ok(*candidate);
+ }
+ }
+ let expected =
+ expected.iter().map(|state| format!("{:?}", state)).collect::<Vec<_>>().join(", ");
+ bail!("\"{s}\": not a valid flag state, expected one of {expected}");
+ }
+
+ let error_msg = format!("\"{filter}\": filter syntax error");
+ let (what, arg) = filter.split_once(':').ok_or_else(|| anyhow!(error_msg.clone()))?;
+ match what {
+ "package" => {
+ let expected = arg.to_owned();
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.package() == expected))
+ }
+ "name" => {
+ let expected = arg.to_owned();
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.name() == expected))
+ }
+ "namespace" => {
+ let expected = arg.to_owned();
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.namespace() == expected))
+ }
+ // description: not supported yet
+ "bug" => {
+ let expected = arg.to_owned();
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.bug.join(", ") == expected))
+ }
+ "state" => {
+ let expected = enum_from_str(&[ProtoFlagState::ENABLED, ProtoFlagState::DISABLED], arg)
+ .context(error_msg)?;
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.state() == expected))
+ }
+ "permission" => {
+ let expected = enum_from_str(
+ &[ProtoFlagPermission::READ_ONLY, ProtoFlagPermission::READ_WRITE],
+ arg,
+ )
+ .context(error_msg)?;
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.permission() == expected))
+ }
+ // trace: not supported yet
+ "is_fixed_read_only" => {
+ let expected: bool = arg.parse().context(error_msg)?;
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_fixed_read_only() == expected))
+ }
+ "is_exported" => {
+ let expected: bool = arg.parse().context(error_msg)?;
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.is_exported() == expected))
+ }
+ "container" => {
+ let expected = arg.to_owned();
+ Ok(Box::new(move |flag: &ProtoParsedFlag| flag.container() == expected))
+ }
+ // metadata: not supported yet
+ _ => Err(anyhow!(error_msg)),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::protos::ProtoParsedFlags;
+ use crate::test::parse_test_flags;
+ use protobuf::Message;
+
+ fn parse_enabled_ro_flag() -> ProtoParsedFlag {
+ parse_test_flags().parsed_flag.into_iter().find(|pf| pf.name() == "enabled_ro").unwrap()
+ }
+
+ #[test]
+ fn test_dumpformat_from_str() {
+ // supported format types
+ assert_eq!(DumpFormat::try_from("protobuf").unwrap(), DumpFormat::Protobuf);
+ assert_eq!(DumpFormat::try_from("textproto").unwrap(), DumpFormat::Textproto);
+ assert_eq!(
+ DumpFormat::try_from("foobar").unwrap(),
+ DumpFormat::Custom("foobar".to_owned())
+ );
+ }
+
+ #[test]
+ fn test_dump_parsed_flags_protobuf_format() {
+ let expected = protobuf::text_format::parse_from_str::<ProtoParsedFlags>(
+ crate::test::TEST_FLAGS_TEXTPROTO,
+ )
+ .unwrap()
+ .write_to_bytes()
+ .unwrap();
+ let parsed_flags = parse_test_flags();
+ let actual =
+ dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Protobuf).unwrap();
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn test_dump_parsed_flags_textproto_format() {
+ let parsed_flags = parse_test_flags();
+ let bytes =
+ dump_parsed_flags(parsed_flags.parsed_flag.into_iter(), DumpFormat::Textproto).unwrap();
+ let text = std::str::from_utf8(&bytes).unwrap();
+ assert_eq!(crate::test::TEST_FLAGS_TEXTPROTO.trim(), text.trim());
+ }
+
+ #[test]
+ fn test_dump_parsed_flags_custom_format() {
+ macro_rules! assert_dump_parsed_flags_custom_format_contains {
+ ($format:expr, $expected:expr) => {
+ let parsed_flags = parse_test_flags();
+ let bytes = dump_parsed_flags(
+ parsed_flags.parsed_flag.into_iter(),
+ $format.try_into().unwrap(),
+ )
+ .unwrap();
+ let text = std::str::from_utf8(&bytes).unwrap();
+ assert!(text.contains($expected));
+ };
+ }
+
+ // custom format
+ assert_dump_parsed_flags_custom_format_contains!(
+ "{fully_qualified_name}={permission} + {state}",
+ "com.android.aconfig.test.enabled_ro=READ_ONLY + ENABLED"
+ );
+
+ // aliases
+ assert_dump_parsed_flags_custom_format_contains!(
+ "text",
+ "com.android.aconfig.test.enabled_ro [system]: READ_ONLY + ENABLED"
+ );
+ assert_dump_parsed_flags_custom_format_contains!(
+ "verbose",
+ "com.android.aconfig.test.enabled_ro [system]: READ_ONLY + ENABLED (tests/test.aconfig, tests/first.values, tests/second.values)"
+ );
+ assert_dump_parsed_flags_custom_format_contains!(
+ "bool",
+ "com.android.aconfig.test.enabled_ro=true"
+ );
+ }
+
+ #[test]
+ fn test_dump_custom_format() {
+ macro_rules! assert_custom_format {
+ ($format:expr, $expected:expr) => {
+ let flag = parse_enabled_ro_flag();
+ let mut bytes = vec![];
+ dump_custom_format(&flag, $format, &mut bytes);
+ let text = std::str::from_utf8(&bytes).unwrap();
+ assert_eq!(text, $expected);
+ };
+ }
+
+ assert_custom_format!("{package}", "com.android.aconfig.test\n");
+ assert_custom_format!("{name}", "enabled_ro\n");
+ assert_custom_format!("{namespace}", "aconfig_test\n");
+ assert_custom_format!("{description}", "This flag is ENABLED + READ_ONLY\n");
+ assert_custom_format!("{bug}", "abc\n");
+ assert_custom_format!("{state}", "ENABLED\n");
+ assert_custom_format!("{state:bool}", "true\n");
+ assert_custom_format!("{permission}", "READ_ONLY\n");
+ assert_custom_format!("{trace}", "tests/test.aconfig: READ_WRITE + DISABLED, tests/first.values: READ_WRITE + DISABLED, tests/second.values: READ_ONLY + ENABLED\n");
+ assert_custom_format!(
+ "{trace:paths}",
+ "tests/test.aconfig, tests/first.values, tests/second.values\n"
+ );
+ assert_custom_format!("{is_fixed_read_only}", "false\n");
+ assert_custom_format!("{is_exported}", "false\n");
+ assert_custom_format!("{container}", "system\n");
+ assert_custom_format!("{metadata}", "PURPOSE_BUGFIX\n");
+
+ assert_custom_format!("name={name}|state={state}", "name=enabled_ro|state=ENABLED\n");
+ assert_custom_format!("{state}{state}{state}", "ENABLEDENABLEDENABLED\n");
+ }
+
+ #[test]
+ fn test_create_filter_predicate() {
+ macro_rules! assert_create_filter_predicate {
+ ($filter:expr, $expected:expr) => {
+ let parsed_flags = parse_test_flags();
+ let predicate = create_filter_predicate($filter).unwrap();
+ let mut filtered_flags: Vec<String> = parsed_flags
+ .parsed_flag
+ .into_iter()
+ .filter(predicate)
+ .map(|flag| flag.fully_qualified_name())
+ .collect();
+ filtered_flags.sort();
+ assert_eq!(&filtered_flags, $expected);
+ };
+ }
+
+ assert_create_filter_predicate!(
+ "package:com.android.aconfig.test",
+ &[
+ "com.android.aconfig.test.disabled_ro",
+ "com.android.aconfig.test.disabled_rw",
+ "com.android.aconfig.test.disabled_rw_exported",
+ "com.android.aconfig.test.disabled_rw_in_other_namespace",
+ "com.android.aconfig.test.enabled_fixed_ro",
+ "com.android.aconfig.test.enabled_ro",
+ "com.android.aconfig.test.enabled_ro_exported",
+ "com.android.aconfig.test.enabled_rw",
+ ]
+ );
+ assert_create_filter_predicate!(
+ "name:disabled_rw",
+ &["com.android.aconfig.test.disabled_rw"]
+ );
+ assert_create_filter_predicate!(
+ "namespace:other_namespace",
+ &["com.android.aconfig.test.disabled_rw_in_other_namespace"]
+ );
+ // description: not supported yet
+ assert_create_filter_predicate!("bug:123", &["com.android.aconfig.test.disabled_ro",]);
+ assert_create_filter_predicate!(
+ "state:ENABLED",
+ &[
+ "com.android.aconfig.test.enabled_fixed_ro",
+ "com.android.aconfig.test.enabled_ro",
+ "com.android.aconfig.test.enabled_ro_exported",
+ "com.android.aconfig.test.enabled_rw",
+ ]
+ );
+ assert_create_filter_predicate!(
+ "permission:READ_ONLY",
+ &[
+ "com.android.aconfig.test.disabled_ro",
+ "com.android.aconfig.test.enabled_fixed_ro",
+ "com.android.aconfig.test.enabled_ro",
+ "com.android.aconfig.test.enabled_ro_exported",
+ ]
+ );
+ // trace: not supported yet
+ assert_create_filter_predicate!(
+ "is_fixed_read_only:true",
+ &["com.android.aconfig.test.enabled_fixed_ro"]
+ );
+ assert_create_filter_predicate!(
+ "is_exported:true",
+ &[
+ "com.android.aconfig.test.disabled_rw_exported",
+ "com.android.aconfig.test.enabled_ro_exported",
+ ]
+ );
+ assert_create_filter_predicate!(
+ "container:system",
+ &[
+ "com.android.aconfig.test.disabled_ro",
+ "com.android.aconfig.test.disabled_rw",
+ "com.android.aconfig.test.disabled_rw_exported",
+ "com.android.aconfig.test.disabled_rw_in_other_namespace",
+ "com.android.aconfig.test.enabled_fixed_ro",
+ "com.android.aconfig.test.enabled_ro",
+ "com.android.aconfig.test.enabled_ro_exported",
+ "com.android.aconfig.test.enabled_rw",
+ ]
+ );
+ // metadata: not supported yet
+
+ // multiple sub filters
+ assert_create_filter_predicate!(
+ "permission:READ_ONLY+state:ENABLED",
+ &[
+ "com.android.aconfig.test.enabled_fixed_ro",
+ "com.android.aconfig.test.enabled_ro",
+ "com.android.aconfig.test.enabled_ro_exported",
+ ]
+ );
+ }
+}
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 63a50c8..fcc5ea5 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -26,13 +26,21 @@
mod codegen;
mod commands;
+mod dump;
mod protos;
mod storage;
+use dump::DumpFormat;
+
#[cfg(test)]
mod test;
-use commands::{CodegenMode, DumpFormat, Input, OutputFile};
+use commands::{CodegenMode, Input, OutputFile};
+
+const HELP_DUMP_FILTER: &str = r#"
+Limit which flags to output. If multiple --filter arguments are provided, the output will be
+limited to flags that match any of the filters.
+"#;
fn cli() -> Command {
Command::new("aconfig")
@@ -103,9 +111,10 @@
.arg(
Arg::new("format")
.long("format")
- .value_parser(EnumValueParser::<commands::DumpFormat>::new())
+ .value_parser(|s: &str| DumpFormat::try_from(s))
.default_value("text"),
)
+ .arg(Arg::new("filter").long("filter").action(ArgAction::Append).help(HELP_DUMP_FILTER.trim()))
.arg(Arg::new("dedup").long("dedup").num_args(0).action(ArgAction::SetTrue))
.arg(Arg::new("out").long("out").default_value("-")),
)
@@ -249,8 +258,13 @@
let input = open_zero_or_more_files(sub_matches, "cache")?;
let format = get_required_arg::<DumpFormat>(sub_matches, "format")
.context("failed to dump previously parsed flags")?;
+ let filters = sub_matches
+ .get_many::<String>("filter")
+ .unwrap_or_default()
+ .map(String::as_ref)
+ .collect::<Vec<_>>();
let dedup = get_required_arg::<bool>(sub_matches, "dedup")?;
- let output = commands::dump_parsed_flags(input, *format, *dedup)?;
+ let output = commands::dump_parsed_flags(input, format.clone(), &filters, *dedup)?;
let path = get_required_arg::<String>(sub_matches, "out")?;
write_output_to_file_or_stdout(path, &output)?;
}
diff --git a/tools/compliance/go.mod b/tools/compliance/go.mod
index 1928189..bd04077 100644
--- a/tools/compliance/go.mod
+++ b/tools/compliance/go.mod
@@ -26,4 +26,4 @@
// Indirect dep from go-cmp
exclude golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
-go 1.18
+go 1.21