Restructure aconfig repo to be a cargo workspace with many crates

This is cherry pick of aosp/2924191 to avoid merge conflict in git main

Previously, aconfig repo is the root directory of aconfig binary crate,
but it also hosts printflags crate inside, and there is no cargo support
for printflags binary crate. In addition, with more aconfig development,
more crates are being added to this repo. Thus this repo should be
configured as a Cargo workspace with multiple crates rather than a
single crate.

Note the top level Cargo.toml file specifies the crates this workspace
carries:
(1) aconfig_protos: the proto library crate that will be used by many other
crates such as aconfig binary crate and printflags binary crate
(2) aconfig: the aconfig binary crate
(3) printflags: the printflags binary crate

(1) aconfig_protos crate setup:

Inside aconfig_protos dir we set up the aconfig_protos crate, the
previously src/proto.rs is now aconfig_protos/src/lib.rs, the build.rs
is carried over to this crate.

(2) aconfig binary crate setup:

Notice its Cargo.toml file claims package dependency on aconfig_protos
crate. It no longer carries proto related module and build.rs file.

(3) printflags binary crate setup:

Similary, notice that in its Cargo.toml file, it claims package
dependency on aconfig_protos crate.

With this setup, we can Cargo build/test each crate individually when
inside a specific crate dir. But we can also run Cargo build/test at
repo root level, which will build/test all the crates in this workplace.

This is the structuring cl. The next cl is to move storage modules into
its own library crate. This storage file library crate will be used by
both aconfig binary crate as well as flag read library crate (to be
created as another new crate here).

Bug: b/321984352
Test: top and individual crate dir level Cargo build/test, m each
individual targets

Ignore-AOSP-First: cherrypick to git main to resolve merge conflict
Change-Id: I75833f4997f7ee554ff6c1557df9ac87f62b2732
This commit is contained in:
Dennis Shen
2024-01-23 18:01:52 +00:00
parent 75bd81de17
commit c77c6612e7
51 changed files with 205 additions and 156 deletions

View File

@@ -1,22 +1,7 @@
[package] [workspace]
name = "aconfig"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[features] members = [
default = ["cargo"] "aconfig",
cargo = [] "aconfig_protos",
"printflags"
[dependencies] ]
anyhow = "1.0.69"
clap = { version = "4.1.8", features = ["derive"] }
itertools = "0.10.5"
paste = "1.0.11"
protobuf = "3.2.0"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93"
tinytemplate = "1.2.1"
[build-dependencies]
protobuf-codegen = "3.2.0"

View File

@@ -2,51 +2,6 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"], default_applicable_licenses: ["Android-Apache-2.0"],
} }
// proto libraries for consumers of `aconfig dump --format=protobuf` output
java_library {
name: "libaconfig_java_proto_lite",
host_supported: true,
srcs: ["protos/aconfig.proto"],
static_libs: ["libprotobuf-java-lite"],
proto: {
type: "lite",
},
sdk_version: "current",
min_sdk_version: "34",
apex_available: [
"com.android.configinfrastructure",
"//apex_available:platform",
]
}
java_library_host {
name: "libaconfig_java_proto_full",
srcs: ["protos/aconfig.proto"],
static_libs: ["libprotobuf-java-full"],
proto: {
type: "full",
},
}
python_library_host {
name: "libaconfig_python_proto",
srcs: ["protos/aconfig.proto"],
proto: {
canonical_path_from_root: false,
},
}
// host binary: aconfig
rust_protobuf {
name: "libaconfig_protos",
protos: ["protos/aconfig.proto"],
crate_name: "aconfig_protos",
source_stem: "aconfig_protos",
host_supported: true,
}
rust_defaults { rust_defaults {
name: "aconfig.defaults", name: "aconfig.defaults",
edition: "2021", edition: "2021",
@@ -63,9 +18,6 @@ rust_defaults {
"libserde_json", "libserde_json",
"libtinytemplate", "libtinytemplate",
], ],
proc_macros: [
"libpaste",
]
} }
rust_binary_host { rust_binary_host {

View File

@@ -0,0 +1,18 @@
[package]
name = "aconfig"
version = "0.1.0"
edition = "2021"
[features]
default = ["cargo"]
cargo = []
[dependencies]
anyhow = "1.0.69"
clap = { version = "4.1.8", features = ["derive"] }
itertools = "0.10.5"
protobuf = "3.2.0"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93"
tinytemplate = "1.2.1"
aconfig_protos = { path = "../aconfig_protos" }

View File

@@ -19,10 +19,11 @@ use serde::Serialize;
use std::path::PathBuf; use std::path::PathBuf;
use tinytemplate::TinyTemplate; use tinytemplate::TinyTemplate;
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
use crate::codegen; use crate::codegen;
use crate::codegen::CodegenMode; use crate::codegen::CodegenMode;
use crate::commands::OutputFile; use crate::commands::OutputFile;
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
pub fn generate_cpp_code<I>( pub fn generate_cpp_code<I>(
package: &str, package: &str,
@@ -136,7 +137,7 @@ fn create_class_element(package: &str, pf: &ProtoParsedFlag, rw_count: &mut i32)
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::protos::ProtoParsedFlags; use aconfig_protos::ProtoParsedFlags;
use std::collections::HashMap; use std::collections::HashMap;
const EXPORTED_PROD_HEADER_EXPECTED: &str = r#" const EXPORTED_PROD_HEADER_EXPECTED: &str = r#"

View File

@@ -20,10 +20,11 @@ use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf; use std::path::PathBuf;
use tinytemplate::TinyTemplate; use tinytemplate::TinyTemplate;
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
use crate::codegen; use crate::codegen;
use crate::codegen::CodegenMode; use crate::codegen::CodegenMode;
use crate::commands::OutputFile; use crate::commands::OutputFile;
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
pub fn generate_java_code<I>( pub fn generate_java_code<I>(
package: &str, package: &str,

View File

@@ -20,32 +20,7 @@ pub mod rust;
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use clap::ValueEnum; use clap::ValueEnum;
use aconfig_protos::{is_valid_name_ident, is_valid_package_ident};
pub fn is_valid_name_ident(s: &str) -> bool {
// Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
if s.contains("__") {
return false;
}
let mut chars = s.chars();
let Some(first) = chars.next() else {
return false;
};
if !first.is_ascii_lowercase() {
return false;
}
chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
}
pub fn is_valid_package_ident(s: &str) -> bool {
if !s.contains('.') {
return false;
}
s.split('.').all(is_valid_name_ident)
}
pub fn is_valid_container_ident(s: &str) -> bool {
s.split('.').all(is_valid_name_ident)
}
pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> { 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(package), "bad package");
@@ -75,6 +50,7 @@ impl std::fmt::Display for CodegenMode {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use aconfig_protos::is_valid_container_ident;
#[test] #[test]
fn test_is_valid_name_ident() { fn test_is_valid_name_ident() {

View File

@@ -18,10 +18,11 @@ use anyhow::Result;
use serde::Serialize; use serde::Serialize;
use tinytemplate::TinyTemplate; use tinytemplate::TinyTemplate;
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
use crate::codegen; use crate::codegen;
use crate::codegen::CodegenMode; use crate::codegen::CodegenMode;
use crate::commands::OutputFile; use crate::commands::OutputFile;
use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
pub fn generate_rust_code<I>( pub fn generate_rust_code<I>(
package: &str, package: &str,

View File

@@ -26,7 +26,7 @@ use crate::codegen::java::generate_java_code;
use crate::codegen::rust::generate_rust_code; use crate::codegen::rust::generate_rust_code;
use crate::codegen::CodegenMode; use crate::codegen::CodegenMode;
use crate::dump::{DumpFormat, DumpPredicate}; use crate::dump::{DumpFormat, DumpPredicate};
use crate::protos::{ use aconfig_protos::{
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag, ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
ProtoParsedFlags, ProtoTracepoint, ProtoParsedFlags, ProtoTracepoint,
}; };
@@ -44,7 +44,7 @@ impl Input {
self.reader self.reader
.read_to_end(&mut buffer) .read_to_end(&mut buffer)
.with_context(|| format!("failed to read {}", self.source))?; .with_context(|| format!("failed to read {}", self.source))?;
crate::protos::parsed_flags::try_from_binary_proto(&buffer) aconfig_protos::parsed_flags::try_from_binary_proto(&buffer)
.with_context(|| self.error_context()) .with_context(|| self.error_context())
} }
@@ -77,7 +77,7 @@ pub fn parse_flags(
.read_to_string(&mut contents) .read_to_string(&mut contents)
.with_context(|| format!("failed to read {}", input.source))?; .with_context(|| format!("failed to read {}", input.source))?;
let flag_declarations = crate::protos::flag_declarations::try_from_text_proto(&contents) let flag_declarations = aconfig_protos::flag_declarations::try_from_text_proto(&contents)
.with_context(|| input.error_context())?; .with_context(|| input.error_context())?;
ensure!( ensure!(
package == flag_declarations.package(), package == flag_declarations.package(),
@@ -96,7 +96,7 @@ pub fn parse_flags(
); );
} }
for mut flag_declaration in flag_declarations.flag.into_iter() { for mut flag_declaration in flag_declarations.flag.into_iter() {
crate::protos::flag_declaration::verify_fields(&flag_declaration) aconfig_protos::flag_declaration::verify_fields(&flag_declaration)
.with_context(|| input.error_context())?; .with_context(|| input.error_context())?;
// create ParsedFlag using FlagDeclaration and default values // create ParsedFlag using FlagDeclaration and default values
@@ -130,7 +130,7 @@ pub fn parse_flags(
parsed_flag.metadata = Some(metadata).into(); parsed_flag.metadata = Some(metadata).into();
// verify ParsedFlag looks reasonable // verify ParsedFlag looks reasonable
crate::protos::parsed_flag::verify_fields(&parsed_flag)?; aconfig_protos::parsed_flag::verify_fields(&parsed_flag)?;
// verify ParsedFlag can be added // verify ParsedFlag can be added
ensure!( ensure!(
@@ -151,10 +151,10 @@ pub fn parse_flags(
.reader .reader
.read_to_string(&mut contents) .read_to_string(&mut contents)
.with_context(|| format!("failed to read {}", input.source))?; .with_context(|| format!("failed to read {}", input.source))?;
let flag_values = crate::protos::flag_values::try_from_text_proto(&contents) let flag_values = aconfig_protos::flag_values::try_from_text_proto(&contents)
.with_context(|| input.error_context())?; .with_context(|| input.error_context())?;
for flag_value in flag_values.flag_value.into_iter() { for flag_value in flag_values.flag_value.into_iter() {
crate::protos::flag_value::verify_fields(&flag_value) aconfig_protos::flag_value::verify_fields(&flag_value)
.with_context(|| input.error_context())?; .with_context(|| input.error_context())?;
let Some(parsed_flag) = parsed_flags let Some(parsed_flag) = parsed_flags
@@ -184,8 +184,8 @@ pub fn parse_flags(
} }
// Create a sorted parsed_flags // Create a sorted parsed_flags
crate::protos::parsed_flags::sort_parsed_flags(&mut parsed_flags); aconfig_protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
crate::protos::parsed_flags::verify_fields(&parsed_flags)?; aconfig_protos::parsed_flags::verify_fields(&parsed_flags)?;
let mut output = Vec::new(); let mut output = Vec::new();
parsed_flags.write_to_vec(&mut output)?; parsed_flags.write_to_vec(&mut output)?;
Ok(output) Ok(output)
@@ -287,7 +287,7 @@ pub fn dump_parsed_flags(
let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> = let individually_parsed_flags: Result<Vec<ProtoParsedFlags>> =
input.iter_mut().map(|i| i.try_parse_flags()).collect(); input.iter_mut().map(|i| i.try_parse_flags()).collect();
let parsed_flags: ProtoParsedFlags = let parsed_flags: ProtoParsedFlags =
crate::protos::parsed_flags::merge(individually_parsed_flags?, dedup)?; aconfig_protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;
let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() { let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() {
vec![Box::new(|_| true)] vec![Box::new(|_| true)]
} else { } else {
@@ -386,16 +386,16 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::protos::ProtoFlagPurpose; use aconfig_protos::ProtoFlagPurpose;
#[test] #[test]
fn test_parse_flags() { fn test_parse_flags() {
let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags let parsed_flags = crate::test::parse_test_flags(); // calls parse_flags
crate::protos::parsed_flags::verify_fields(&parsed_flags).unwrap(); aconfig_protos::parsed_flags::verify_fields(&parsed_flags).unwrap();
let enabled_ro = let enabled_ro =
parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_ro").unwrap(); parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_ro").unwrap();
assert!(crate::protos::parsed_flag::verify_fields(enabled_ro).is_ok()); assert!(aconfig_protos::parsed_flag::verify_fields(enabled_ro).is_ok());
assert_eq!("com.android.aconfig.test", enabled_ro.package()); assert_eq!("com.android.aconfig.test", enabled_ro.package());
assert_eq!("enabled_ro", enabled_ro.name()); assert_eq!("enabled_ro", enabled_ro.name());
assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description()); assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description());
@@ -462,7 +462,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let parsed_flags = let parsed_flags =
crate::protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap(); aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
assert_eq!(1, parsed_flags.parsed_flag.len()); assert_eq!(1, parsed_flags.parsed_flag.len());
let parsed_flag = parsed_flags.parsed_flag.first().unwrap(); let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state()); assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());
@@ -602,7 +602,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let parsed_flags = let parsed_flags =
crate::protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap(); aconfig_protos::parsed_flags::try_from_binary_proto(&flags_bytes).unwrap();
assert_eq!(1, parsed_flags.parsed_flag.len()); assert_eq!(1, parsed_flags.parsed_flag.len());
let parsed_flag = parsed_flags.parsed_flag.first().unwrap(); let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose()); assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose());

View File

@@ -14,10 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
use crate::protos::{ use aconfig_protos::{
ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint, ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint,
}; };
use crate::protos::{ProtoParsedFlag, ProtoParsedFlags}; use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use protobuf::Message; use protobuf::Message;
@@ -197,7 +197,7 @@ fn create_filter_predicate_single(filter: &str) -> Result<Box<DumpPredicate>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::protos::ProtoParsedFlags; use aconfig_protos::ProtoParsedFlags;
use crate::test::parse_test_flags; use crate::test::parse_test_flags;
use protobuf::Message; use protobuf::Message;

View File

@@ -27,7 +27,6 @@ use std::path::{Path, PathBuf};
mod codegen; mod codegen;
mod commands; mod commands;
mod dump; mod dump;
mod protos;
mod storage; mod storage;
use codegen::CodegenMode; use codegen::CodegenMode;
@@ -57,8 +56,8 @@ fn cli() -> Command {
.arg( .arg(
Arg::new("default-permission") Arg::new("default-permission")
.long("default-permission") .long("default-permission")
.value_parser(protos::flag_permission::parse_from_str) .value_parser(aconfig_protos::flag_permission::parse_from_str)
.default_value(protos::flag_permission::to_string( .default_value(aconfig_protos::flag_permission::to_string(
&commands::DEFAULT_FLAG_PERMISSION, &commands::DEFAULT_FLAG_PERMISSION,
)), )),
) )
@@ -215,7 +214,7 @@ fn main() -> Result<()> {
let declarations = open_zero_or_more_files(sub_matches, "declarations")?; let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
let values = open_zero_or_more_files(sub_matches, "values")?; let values = open_zero_or_more_files(sub_matches, "values")?;
let default_permission = let default_permission =
get_required_arg::<protos::ProtoFlagPermission>(sub_matches, "default-permission")?; get_required_arg::<aconfig_protos::ProtoFlagPermission>(sub_matches, "default-permission")?;
let output = commands::parse_flags( let output = commands::parse_flags(
package, package,
container, container,

View File

@@ -15,7 +15,7 @@
*/ */
use crate::commands::assign_flag_ids; use crate::commands::assign_flag_ids;
use crate::protos::ProtoFlagState; use aconfig_protos::ProtoFlagState;
use crate::storage::{self, FlagPackage}; use crate::storage::{self, FlagPackage};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};

View File

@@ -22,7 +22,7 @@ use anyhow::{anyhow, Result};
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet}; use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use crate::protos::{ProtoParsedFlag, ProtoParsedFlags}; use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
use crate::storage::{ use crate::storage::{
flag_table::FlagTable, flag_value::FlagValueList, package_table::PackageTable, flag_table::FlagTable, flag_value::FlagValueList, package_table::PackageTable,
}; };
@@ -221,7 +221,7 @@ mod tests {
crate::commands::DEFAULT_FLAG_PERMISSION, crate::commands::DEFAULT_FLAG_PERMISSION,
) )
.unwrap(); .unwrap();
crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
}) })
.collect() .collect()
} }

View File

@@ -17,7 +17,7 @@
#[cfg(test)] #[cfg(test)]
pub mod test_utils { pub mod test_utils {
use crate::commands::Input; use crate::commands::Input;
use crate::protos::ProtoParsedFlags; use aconfig_protos::ProtoParsedFlags;
use itertools; use itertools;
pub const TEST_PACKAGE: &str = "com.android.aconfig.test"; pub const TEST_PACKAGE: &str = "com.android.aconfig.test";
@@ -265,7 +265,7 @@ parsed_flag {
crate::commands::DEFAULT_FLAG_PERMISSION, crate::commands::DEFAULT_FLAG_PERMISSION,
) )
.unwrap(); .unwrap();
crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
} }
pub fn parse_test_flags() -> ProtoParsedFlags { pub fn parse_test_flags() -> ProtoParsedFlags {
@@ -289,7 +289,7 @@ parsed_flag {
crate::commands::DEFAULT_FLAG_PERMISSION, crate::commands::DEFAULT_FLAG_PERMISSION,
) )
.unwrap(); .unwrap();
crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap() aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
} }
pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> { pub fn first_significant_code_diff(a: &str, b: &str) -> Option<String> {

View File

@@ -0,0 +1,62 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
// proto libraries for consumers of `aconfig dump --format=protobuf` output
java_library {
name: "libaconfig_java_proto_lite",
host_supported: true,
srcs: ["protos/aconfig.proto"],
static_libs: ["libprotobuf-java-lite"],
proto: {
type: "lite",
},
sdk_version: "current",
min_sdk_version: "UpsideDownCake",
apex_available: [
"com.android.configinfrastructure",
"//apex_available:platform",
]
}
java_library_host {
name: "libaconfig_java_proto_full",
srcs: ["protos/aconfig.proto"],
static_libs: ["libprotobuf-java-full"],
proto: {
type: "full",
},
}
python_library_host {
name: "libaconfig_python_proto",
srcs: ["protos/aconfig.proto"],
proto: {
canonical_path_from_root: false,
},
}
rust_protobuf {
name: "libaconfig_rust_proto",
protos: ["protos/aconfig.proto"],
crate_name: "aconfig_rust_proto",
source_stem: "aconfig_rust_proto",
host_supported: true,
}
rust_library {
name: "libaconfig_protos",
srcs: ["src/lib.rs"],
crate_name: "aconfig_protos",
host_supported: true,
lints: "none",
rustlibs: [
"libaconfig_rust_proto",
"libanyhow",
"libprotobuf",
],
proc_macros: [
"libpaste",
]
}

View File

@@ -0,0 +1,17 @@
[package]
name = "aconfig_protos"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[features]
default = ["cargo"]
cargo = []
[dependencies]
anyhow = "1.0.69"
paste = "1.0.11"
protobuf = "3.2.0"
[build-dependencies]
protobuf-codegen = "3.2.0"

View File

@@ -29,17 +29,17 @@
// ---- When building with the Android tool-chain ---- // ---- When building with the Android tool-chain ----
#[cfg(not(feature = "cargo"))] #[cfg(not(feature = "cargo"))]
mod auto_generated { mod auto_generated {
pub use aconfig_protos::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose; pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration; pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;
pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations; pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;
pub use aconfig_protos::aconfig::Flag_metadata as ProtoFlagMetadata; pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;
pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission; pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;
pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState; pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;
pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue; pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;
pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues; pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;
pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag; pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;
pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags; pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;
pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint; pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;
} }
// ---- When building with cargo ---- // ---- When building with cargo ----
@@ -68,6 +68,32 @@ pub use auto_generated::*;
use anyhow::Result; use anyhow::Result;
use paste::paste; use paste::paste;
pub fn is_valid_name_ident(s: &str) -> bool {
// Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
if s.contains("__") {
return false;
}
let mut chars = s.chars();
let Some(first) = chars.next() else {
return false;
};
if !first.is_ascii_lowercase() {
return false;
}
chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
}
pub fn is_valid_package_ident(s: &str) -> bool {
if !s.contains('.') {
return false;
}
s.split('.').all(is_valid_name_ident)
}
pub fn is_valid_container_ident(s: &str) -> bool {
s.split('.').all(is_valid_name_ident)
}
fn try_from_text_proto<T>(s: &str) -> Result<T> fn try_from_text_proto<T>(s: &str) -> Result<T>
where where
T: protobuf::MessageFull, T: protobuf::MessageFull,
@@ -87,14 +113,13 @@ macro_rules! ensure_required_fields {
pub mod flag_declaration { pub mod flag_declaration {
use super::*; use super::*;
use crate::codegen;
use anyhow::ensure; use anyhow::ensure;
pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> { pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description"); ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name"); ensure!(is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name"); ensure!(is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description"); ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required"); ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
@@ -104,7 +129,6 @@ pub mod flag_declaration {
pub mod flag_declarations { pub mod flag_declarations {
use super::*; use super::*;
use crate::codegen;
use anyhow::ensure; use anyhow::ensure;
pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> { pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
@@ -118,11 +142,11 @@ pub mod flag_declarations {
// TODO(b/312769710): Make the container field required. // TODO(b/312769710): Make the container field required.
ensure!( ensure!(
codegen::is_valid_package_ident(pdf.package()), is_valid_package_ident(pdf.package()),
"bad flag declarations: bad package" "bad flag declarations: bad package"
); );
ensure!( ensure!(
!pdf.has_container() || codegen::is_valid_container_ident(pdf.container()), !pdf.has_container() || is_valid_container_ident(pdf.container()),
"bad flag declarations: bad container" "bad flag declarations: bad container"
); );
for flag_declaration in pdf.flag.iter() { for flag_declaration in pdf.flag.iter() {
@@ -135,14 +159,13 @@ pub mod flag_declarations {
pub mod flag_value { pub mod flag_value {
use super::*; use super::*;
use crate::codegen;
use anyhow::ensure; use anyhow::ensure;
pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> { pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
ensure_required_fields!("flag value", fv, "package", "name", "state", "permission"); ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package"); ensure!(is_valid_package_ident(fv.package()), "bad flag value: bad package");
ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name"); ensure!(is_valid_name_ident(fv.name()), "bad flag value: bad name");
Ok(()) Ok(())
} }
@@ -200,7 +223,6 @@ pub mod tracepoint {
pub mod parsed_flag { pub mod parsed_flag {
use super::*; use super::*;
use crate::codegen;
use anyhow::ensure; use anyhow::ensure;
pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> { pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
@@ -215,13 +237,13 @@ pub mod parsed_flag {
"permission" "permission"
); );
ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package"); ensure!(is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
ensure!( ensure!(
!pf.has_container() || codegen::is_valid_container_ident(pf.container()), !pf.has_container() || is_valid_container_ident(pf.container()),
"bad parsed flag: bad container" "bad parsed flag: bad container"
); );
ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name"); ensure!(is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace"); ensure!(is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
ensure!(!pf.description().is_empty(), "bad parsed flag: empty description"); ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace"); ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
for tp in pf.trace.iter() { for tp in pf.trace.iter() {
@@ -261,7 +283,7 @@ pub mod parsed_flags {
} }
pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> { pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
use crate::protos::parsed_flag::path_to_declaration; use crate::parsed_flag::path_to_declaration;
let mut previous: Option<&ProtoParsedFlag> = None; let mut previous: Option<&ProtoParsedFlag> = None;
for parsed_flag in pf.parsed_flag.iter() { for parsed_flag in pf.parsed_flag.iter() {
@@ -848,7 +870,7 @@ parsed_flag {
let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap(); let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
let parsed_flag = &parsed_flags.parsed_flag[0]; let parsed_flag = &parsed_flags.parsed_flag[0];
assert_eq!( assert_eq!(
crate::protos::parsed_flag::path_to_declaration(parsed_flag), crate::parsed_flag::path_to_declaration(parsed_flag),
"flags.declarations" "flags.declarations"
); );
} }

View File

@@ -0,0 +1,15 @@
[package]
name = "printflags"
version = "0.1.0"
edition = "2021"
[features]
default = ["cargo"]
cargo = []
[dependencies]
anyhow = "1.0.69"
paste = "1.0.11"
protobuf = "3.2.0"
regex = "1.10.3"
aconfig_protos = { path = "../aconfig_protos" }

View File

@@ -16,8 +16,8 @@
//! `printflags` is a device binary to print feature flags. //! `printflags` is a device binary to print feature flags.
use aconfig_protos::aconfig::Flag_state as State; use aconfig_protos::ProtoFlagState as State;
use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags; use aconfig_protos::ProtoParsedFlags as ProtoParsedFlags;
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use regex::Regex; use regex::Regex;
use std::collections::BTreeMap; use std::collections::BTreeMap;