Restructure aconfig repo to be a cargo workspace with many crates

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

Change-Id: I75833f4997f7ee554ff6c1557df9ac87f62b2732
Merged-In: I75833f4997f7ee554ff6c1557df9ac87f62b2732
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 7b58e94..8d93261 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -1,22 +1,7 @@
-[package]
-name = "aconfig"
-version = "0.1.0"
-edition = "2021"
-build = "build.rs"
+[workspace]
 
-[features]
-default = ["cargo"]
-cargo = []
-
-[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"
+members = [
+    "aconfig",
+    "aconfig_protos",
+    "printflags"
+]
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/aconfig/Android.bp
similarity index 85%
rename from tools/aconfig/Android.bp
rename to tools/aconfig/aconfig/Android.bp
index d5b5b8f..3be456c 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/aconfig/Android.bp
@@ -2,51 +2,6 @@
     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,
-    },
-}
-
-// 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 {
     name: "aconfig.defaults",
     edition: "2021",
@@ -63,9 +18,6 @@
         "libserde_json",
         "libtinytemplate",
     ],
-    proc_macros: [
-        "libpaste",
-    ]
 }
 
 rust_binary_host {
diff --git a/tools/aconfig/aconfig/Cargo.toml b/tools/aconfig/aconfig/Cargo.toml
new file mode 100644
index 0000000..01ad8c6
--- /dev/null
+++ b/tools/aconfig/aconfig/Cargo.toml
@@ -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" }
diff --git a/tools/aconfig/src/codegen/cpp.rs b/tools/aconfig/aconfig/src/codegen/cpp.rs
similarity index 99%
rename from tools/aconfig/src/codegen/cpp.rs
rename to tools/aconfig/aconfig/src/codegen/cpp.rs
index 1279d8e..cd71b10 100644
--- a/tools/aconfig/src/codegen/cpp.rs
+++ b/tools/aconfig/aconfig/src/codegen/cpp.rs
@@ -19,10 +19,11 @@
 use std::path::PathBuf;
 use tinytemplate::TinyTemplate;
 
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+
 use crate::codegen;
 use crate::codegen::CodegenMode;
 use crate::commands::OutputFile;
-use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
 
 pub fn generate_cpp_code<I>(
     package: &str,
@@ -136,7 +137,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::protos::ProtoParsedFlags;
+    use aconfig_protos::ProtoParsedFlags;
     use std::collections::HashMap;
 
     const EXPORTED_PROD_HEADER_EXPECTED: &str = r#"
diff --git a/tools/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs
similarity index 99%
rename from tools/aconfig/src/codegen/java.rs
rename to tools/aconfig/aconfig/src/codegen/java.rs
index 78e892b..7ce1d51 100644
--- a/tools/aconfig/src/codegen/java.rs
+++ b/tools/aconfig/aconfig/src/codegen/java.rs
@@ -20,10 +20,11 @@
 use std::path::PathBuf;
 use tinytemplate::TinyTemplate;
 
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+
 use crate::codegen;
 use crate::codegen::CodegenMode;
 use crate::commands::OutputFile;
-use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
 
 pub fn generate_java_code<I>(
     package: &str,
diff --git a/tools/aconfig/src/codegen/mod.rs b/tools/aconfig/aconfig/src/codegen/mod.rs
similarity index 86%
rename from tools/aconfig/src/codegen/mod.rs
rename to tools/aconfig/aconfig/src/codegen/mod.rs
index 64ffa8b..7b2336f 100644
--- a/tools/aconfig/src/codegen/mod.rs
+++ b/tools/aconfig/aconfig/src/codegen/mod.rs
@@ -20,32 +20,7 @@
 
 use anyhow::{ensure, Result};
 use clap::ValueEnum;
-
-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)
-}
+use aconfig_protos::{is_valid_name_ident, is_valid_package_ident};
 
 pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> {
     ensure!(is_valid_package_ident(package), "bad package");
@@ -75,6 +50,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
+    use aconfig_protos::is_valid_container_ident;
 
     #[test]
     fn test_is_valid_name_ident() {
diff --git a/tools/aconfig/src/codegen/rust.rs b/tools/aconfig/aconfig/src/codegen/rust.rs
similarity index 99%
rename from tools/aconfig/src/codegen/rust.rs
rename to tools/aconfig/aconfig/src/codegen/rust.rs
index 8a88ffe..33c3d37 100644
--- a/tools/aconfig/src/codegen/rust.rs
+++ b/tools/aconfig/aconfig/src/codegen/rust.rs
@@ -18,10 +18,11 @@
 use serde::Serialize;
 use tinytemplate::TinyTemplate;
 
+use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
+
 use crate::codegen;
 use crate::codegen::CodegenMode;
 use crate::commands::OutputFile;
-use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
 
 pub fn generate_rust_code<I>(
     package: &str,
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs
similarity index 95%
rename from tools/aconfig/src/commands.rs
rename to tools/aconfig/aconfig/src/commands.rs
index a35ad08..93bc436 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/aconfig/src/commands.rs
@@ -26,7 +26,7 @@
 use crate::codegen::rust::generate_rust_code;
 use crate::codegen::CodegenMode;
 use crate::dump::{DumpFormat, DumpPredicate};
-use crate::protos::{
+use aconfig_protos::{
     ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
     ProtoParsedFlags, ProtoTracepoint,
 };
@@ -44,7 +44,7 @@
         self.reader
             .read_to_end(&mut buffer)
             .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())
     }
 
@@ -77,7 +77,7 @@
             .read_to_string(&mut contents)
             .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())?;
         ensure!(
             package == flag_declarations.package(),
@@ -96,7 +96,7 @@
             );
         }
         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())?;
 
             // create ParsedFlag using FlagDeclaration and default values
@@ -130,7 +130,7 @@
             parsed_flag.metadata = Some(metadata).into();
 
             // 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
             ensure!(
@@ -151,10 +151,10 @@
             .reader
             .read_to_string(&mut contents)
             .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())?;
         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())?;
 
             let Some(parsed_flag) = parsed_flags
@@ -184,8 +184,8 @@
     }
 
     // Create a sorted parsed_flags
-    crate::protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
-    crate::protos::parsed_flags::verify_fields(&parsed_flags)?;
+    aconfig_protos::parsed_flags::sort_parsed_flags(&mut parsed_flags);
+    aconfig_protos::parsed_flags::verify_fields(&parsed_flags)?;
     let mut output = Vec::new();
     parsed_flags.write_to_vec(&mut output)?;
     Ok(output)
@@ -287,7 +287,7 @@
     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)?;
+        aconfig_protos::parsed_flags::merge(individually_parsed_flags?, dedup)?;
     let filters: Vec<Box<DumpPredicate>> = if filters.is_empty() {
         vec![Box::new(|_| true)]
     } else {
@@ -386,16 +386,16 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::protos::ProtoFlagPurpose;
+    use aconfig_protos::ProtoFlagPurpose;
 
     #[test]
     fn test_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 =
             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!("enabled_ro", enabled_ro.name());
         assert_eq!("This flag is ENABLED + READ_ONLY", enabled_ro.description());
@@ -462,7 +462,7 @@
         )
         .unwrap();
         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());
         let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
         assert_eq!(ProtoFlagState::DISABLED, parsed_flag.state());
@@ -602,7 +602,7 @@
         )
         .unwrap();
         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());
         let parsed_flag = parsed_flags.parsed_flag.first().unwrap();
         assert_eq!(ProtoFlagPurpose::PURPOSE_FEATURE, parsed_flag.metadata.purpose());
diff --git a/tools/aconfig/src/dump.rs b/tools/aconfig/aconfig/src/dump.rs
similarity index 98%
rename from tools/aconfig/src/dump.rs
rename to tools/aconfig/aconfig/src/dump.rs
index 37368ee..12352f9 100644
--- a/tools/aconfig/src/dump.rs
+++ b/tools/aconfig/aconfig/src/dump.rs
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-use crate::protos::{
+use aconfig_protos::{
     ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoTracepoint,
 };
-use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
+use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
 use anyhow::{anyhow, bail, Context, Result};
 use protobuf::Message;
 
@@ -197,7 +197,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::protos::ProtoParsedFlags;
+    use aconfig_protos::ProtoParsedFlags;
     use crate::test::parse_test_flags;
     use protobuf::Message;
 
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs
similarity index 97%
rename from tools/aconfig/src/main.rs
rename to tools/aconfig/aconfig/src/main.rs
index 120e98c..30a7e9d 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/aconfig/src/main.rs
@@ -27,7 +27,6 @@
 mod codegen;
 mod commands;
 mod dump;
-mod protos;
 mod storage;
 
 use codegen::CodegenMode;
@@ -57,8 +56,8 @@
                 .arg(
                     Arg::new("default-permission")
                         .long("default-permission")
-                        .value_parser(protos::flag_permission::parse_from_str)
-                        .default_value(protos::flag_permission::to_string(
+                        .value_parser(aconfig_protos::flag_permission::parse_from_str)
+                        .default_value(aconfig_protos::flag_permission::to_string(
                             &commands::DEFAULT_FLAG_PERMISSION,
                         )),
                 )
@@ -215,7 +214,7 @@
             let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
             let values = open_zero_or_more_files(sub_matches, "values")?;
             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(
                 package,
                 container,
diff --git a/tools/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
similarity index 100%
rename from tools/aconfig/src/storage/flag_table.rs
rename to tools/aconfig/aconfig/src/storage/flag_table.rs
diff --git a/tools/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
similarity index 99%
rename from tools/aconfig/src/storage/flag_value.rs
rename to tools/aconfig/aconfig/src/storage/flag_value.rs
index 45f5ec0..3c5bb17 100644
--- a/tools/aconfig/src/storage/flag_value.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -15,7 +15,7 @@
  */
 
 use crate::commands::assign_flag_ids;
-use crate::protos::ProtoFlagState;
+use aconfig_protos::ProtoFlagState;
 use crate::storage::{self, FlagPackage};
 use anyhow::{anyhow, Result};
 
diff --git a/tools/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
similarity index 98%
rename from tools/aconfig/src/storage/mod.rs
rename to tools/aconfig/aconfig/src/storage/mod.rs
index b4a8b5e..4f2dc81 100644
--- a/tools/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -22,7 +22,7 @@
 use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
 use std::hash::{Hash, Hasher};
 
-use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
+use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
 use crate::storage::{
     flag_table::FlagTable, flag_value::FlagValueList, package_table::PackageTable,
 };
@@ -221,7 +221,7 @@
                     crate::commands::DEFAULT_FLAG_PERMISSION,
                 )
                 .unwrap();
-                crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
+                aconfig_protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
             })
             .collect()
     }
diff --git a/tools/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
similarity index 100%
rename from tools/aconfig/src/storage/package_table.rs
rename to tools/aconfig/aconfig/src/storage/package_table.rs
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/aconfig/src/test.rs
similarity index 97%
rename from tools/aconfig/src/test.rs
rename to tools/aconfig/aconfig/src/test.rs
index cbb95b8..7b5318d 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/aconfig/src/test.rs
@@ -17,7 +17,7 @@
 #[cfg(test)]
 pub mod test_utils {
     use crate::commands::Input;
-    use crate::protos::ProtoParsedFlags;
+    use aconfig_protos::ProtoParsedFlags;
     use itertools;
 
     pub const TEST_PACKAGE: &str = "com.android.aconfig.test";
@@ -265,7 +265,7 @@
             crate::commands::DEFAULT_FLAG_PERMISSION,
         )
         .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 {
@@ -289,7 +289,7 @@
             crate::commands::DEFAULT_FLAG_PERMISSION,
         )
         .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> {
diff --git a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
similarity index 100%
rename from tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
rename to tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template
diff --git a/tools/aconfig/templates/FeatureFlags.java.template b/tools/aconfig/aconfig/templates/FeatureFlags.java.template
similarity index 100%
rename from tools/aconfig/templates/FeatureFlags.java.template
rename to tools/aconfig/aconfig/templates/FeatureFlags.java.template
diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
similarity index 100%
rename from tools/aconfig/templates/FeatureFlagsImpl.java.template
rename to tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template
diff --git a/tools/aconfig/templates/Flags.java.template b/tools/aconfig/aconfig/templates/Flags.java.template
similarity index 100%
rename from tools/aconfig/templates/Flags.java.template
rename to tools/aconfig/aconfig/templates/Flags.java.template
diff --git a/tools/aconfig/templates/cpp_exported_header.template b/tools/aconfig/aconfig/templates/cpp_exported_header.template
similarity index 100%
rename from tools/aconfig/templates/cpp_exported_header.template
rename to tools/aconfig/aconfig/templates/cpp_exported_header.template
diff --git a/tools/aconfig/templates/cpp_source_file.template b/tools/aconfig/aconfig/templates/cpp_source_file.template
similarity index 100%
rename from tools/aconfig/templates/cpp_source_file.template
rename to tools/aconfig/aconfig/templates/cpp_source_file.template
diff --git a/tools/aconfig/templates/rust.template b/tools/aconfig/aconfig/templates/rust.template
similarity index 100%
rename from tools/aconfig/templates/rust.template
rename to tools/aconfig/aconfig/templates/rust.template
diff --git a/tools/aconfig/templates/rust_test.template b/tools/aconfig/aconfig/templates/rust_test.template
similarity index 100%
rename from tools/aconfig/templates/rust_test.template
rename to tools/aconfig/aconfig/templates/rust_test.template
diff --git a/tools/aconfig/tests/AconfigHostTest.java b/tools/aconfig/aconfig/tests/AconfigHostTest.java
similarity index 100%
rename from tools/aconfig/tests/AconfigHostTest.java
rename to tools/aconfig/aconfig/tests/AconfigHostTest.java
diff --git a/tools/aconfig/tests/AconfigTest.java b/tools/aconfig/aconfig/tests/AconfigTest.java
similarity index 100%
rename from tools/aconfig/tests/AconfigTest.java
rename to tools/aconfig/aconfig/tests/AconfigTest.java
diff --git a/tools/aconfig/tests/AndroidManifest.xml b/tools/aconfig/aconfig/tests/AndroidManifest.xml
similarity index 100%
rename from tools/aconfig/tests/AndroidManifest.xml
rename to tools/aconfig/aconfig/tests/AndroidManifest.xml
diff --git a/tools/aconfig/tests/aconfig_exported_mode_test.cpp b/tools/aconfig/aconfig/tests/aconfig_exported_mode_test.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_exported_mode_test.cpp
rename to tools/aconfig/aconfig/tests/aconfig_exported_mode_test.cpp
diff --git a/tools/aconfig/tests/aconfig_exported_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_exported_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_exported_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_exported_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_force_read_only_mode_test.cpp b/tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_force_read_only_mode_test.cpp
rename to tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.cpp
diff --git a/tools/aconfig/tests/aconfig_force_read_only_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_force_read_only_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_force_read_only_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_prod_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_prod_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_prod_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_prod_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_test.cpp b/tools/aconfig/aconfig/tests/aconfig_test.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_test.cpp
rename to tools/aconfig/aconfig/tests/aconfig_test.cpp
diff --git a/tools/aconfig/tests/aconfig_test_mode_test.rs b/tools/aconfig/aconfig/tests/aconfig_test_mode_test.rs
similarity index 100%
rename from tools/aconfig/tests/aconfig_test_mode_test.rs
rename to tools/aconfig/aconfig/tests/aconfig_test_mode_test.rs
diff --git a/tools/aconfig/tests/aconfig_test_test_variant.cpp b/tools/aconfig/aconfig/tests/aconfig_test_test_variant.cpp
similarity index 100%
rename from tools/aconfig/tests/aconfig_test_test_variant.cpp
rename to tools/aconfig/aconfig/tests/aconfig_test_test_variant.cpp
diff --git a/tools/aconfig/tests/first.values b/tools/aconfig/aconfig/tests/first.values
similarity index 100%
rename from tools/aconfig/tests/first.values
rename to tools/aconfig/aconfig/tests/first.values
diff --git a/tools/aconfig/tests/read_only_test.aconfig b/tools/aconfig/aconfig/tests/read_only_test.aconfig
similarity index 100%
rename from tools/aconfig/tests/read_only_test.aconfig
rename to tools/aconfig/aconfig/tests/read_only_test.aconfig
diff --git a/tools/aconfig/tests/read_only_test.values b/tools/aconfig/aconfig/tests/read_only_test.values
similarity index 100%
rename from tools/aconfig/tests/read_only_test.values
rename to tools/aconfig/aconfig/tests/read_only_test.values
diff --git a/tools/aconfig/tests/second.values b/tools/aconfig/aconfig/tests/second.values
similarity index 100%
rename from tools/aconfig/tests/second.values
rename to tools/aconfig/aconfig/tests/second.values
diff --git a/tools/aconfig/tests/storage_test_1.aconfig b/tools/aconfig/aconfig/tests/storage_test_1.aconfig
similarity index 100%
rename from tools/aconfig/tests/storage_test_1.aconfig
rename to tools/aconfig/aconfig/tests/storage_test_1.aconfig
diff --git a/tools/aconfig/tests/storage_test_2.aconfig b/tools/aconfig/aconfig/tests/storage_test_2.aconfig
similarity index 100%
rename from tools/aconfig/tests/storage_test_2.aconfig
rename to tools/aconfig/aconfig/tests/storage_test_2.aconfig
diff --git a/tools/aconfig/tests/storage_test_4.aconfig b/tools/aconfig/aconfig/tests/storage_test_4.aconfig
similarity index 100%
rename from tools/aconfig/tests/storage_test_4.aconfig
rename to tools/aconfig/aconfig/tests/storage_test_4.aconfig
diff --git a/tools/aconfig/tests/test.aconfig b/tools/aconfig/aconfig/tests/test.aconfig
similarity index 100%
rename from tools/aconfig/tests/test.aconfig
rename to tools/aconfig/aconfig/tests/test.aconfig
diff --git a/tools/aconfig/tests/test_exported.aconfig b/tools/aconfig/aconfig/tests/test_exported.aconfig
similarity index 100%
rename from tools/aconfig/tests/test_exported.aconfig
rename to tools/aconfig/aconfig/tests/test_exported.aconfig
diff --git a/tools/aconfig/tests/test_force_read_only.aconfig b/tools/aconfig/aconfig/tests/test_force_read_only.aconfig
similarity index 100%
rename from tools/aconfig/tests/test_force_read_only.aconfig
rename to tools/aconfig/aconfig/tests/test_force_read_only.aconfig
diff --git a/tools/aconfig/aconfig_protos/Android.bp b/tools/aconfig/aconfig_protos/Android.bp
new file mode 100644
index 0000000..1cc4e41
--- /dev/null
+++ b/tools/aconfig/aconfig_protos/Android.bp
@@ -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",
+    ]
+}
diff --git a/tools/aconfig/aconfig_protos/Cargo.toml b/tools/aconfig/aconfig_protos/Cargo.toml
new file mode 100644
index 0000000..114cf80
--- /dev/null
+++ b/tools/aconfig/aconfig_protos/Cargo.toml
@@ -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"
diff --git a/tools/aconfig/build.rs b/tools/aconfig/aconfig_protos/build.rs
similarity index 100%
rename from tools/aconfig/build.rs
rename to tools/aconfig/aconfig_protos/build.rs
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/aconfig_protos/protos/aconfig.proto
similarity index 100%
rename from tools/aconfig/protos/aconfig.proto
rename to tools/aconfig/aconfig_protos/protos/aconfig.proto
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/aconfig_protos/src/lib.rs
similarity index 91%
rename from tools/aconfig/src/protos.rs
rename to tools/aconfig/aconfig_protos/src/lib.rs
index 2684d20..f0d27d6 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/aconfig_protos/src/lib.rs
@@ -29,17 +29,17 @@
 // ---- When building with the Android tool-chain ----
 #[cfg(not(feature = "cargo"))]
 mod auto_generated {
-    pub use aconfig_protos::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
-    pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
-    pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
-    pub use aconfig_protos::aconfig::Flag_metadata as ProtoFlagMetadata;
-    pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
-    pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
-    pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
-    pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
-    pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
-    pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
-    pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
+    pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
+    pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;
+    pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;
+    pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;
+    pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;
+    pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;
+    pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;
+    pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;
+    pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;
+    pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;
+    pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;
 }
 
 // ---- When building with cargo ----
@@ -68,6 +68,32 @@
 use anyhow::Result;
 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>
 where
     T: protobuf::MessageFull,
@@ -87,14 +113,13 @@
 
 pub mod flag_declaration {
     use super::*;
-    use crate::codegen;
     use anyhow::ensure;
 
     pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
         ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
 
-        ensure!(codegen::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.name()), "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.bug.len() == 1, "bad flag declaration: exactly one bug required");
 
@@ -104,7 +129,6 @@
 
 pub mod flag_declarations {
     use super::*;
-    use crate::codegen;
     use anyhow::ensure;
 
     pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
@@ -118,11 +142,11 @@
         // TODO(b/312769710): Make the container field required.
 
         ensure!(
-            codegen::is_valid_package_ident(pdf.package()),
+            is_valid_package_ident(pdf.package()),
             "bad flag declarations: bad package"
         );
         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"
         );
         for flag_declaration in pdf.flag.iter() {
@@ -135,14 +159,13 @@
 
 pub mod flag_value {
     use super::*;
-    use crate::codegen;
     use anyhow::ensure;
 
     pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
         ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
 
-        ensure!(codegen::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_package_ident(fv.package()), "bad flag value: bad package");
+        ensure!(is_valid_name_ident(fv.name()), "bad flag value: bad name");
 
         Ok(())
     }
@@ -200,7 +223,6 @@
 
 pub mod parsed_flag {
     use super::*;
-    use crate::codegen;
     use anyhow::ensure;
 
     pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
@@ -215,13 +237,13 @@
             "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!(
-            !pf.has_container() || codegen::is_valid_container_ident(pf.container()),
+            !pf.has_container() || is_valid_container_ident(pf.container()),
             "bad parsed flag: bad container"
         );
-        ensure!(codegen::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.name()), "bad parsed flag: bad name");
+        ensure!(is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
         ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
         ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
         for tp in pf.trace.iter() {
@@ -261,7 +283,7 @@
     }
 
     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;
         for parsed_flag in pf.parsed_flag.iter() {
@@ -848,7 +870,7 @@
         let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
         let parsed_flag = &parsed_flags.parsed_flag[0];
         assert_eq!(
-            crate::protos::parsed_flag::path_to_declaration(parsed_flag),
+            crate::parsed_flag::path_to_declaration(parsed_flag),
             "flags.declarations"
         );
     }
diff --git a/tools/aconfig/printflags/Cargo.toml b/tools/aconfig/printflags/Cargo.toml
new file mode 100644
index 0000000..7313f5d
--- /dev/null
+++ b/tools/aconfig/printflags/Cargo.toml
@@ -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" }
diff --git a/tools/aconfig/printflags/src/main.rs b/tools/aconfig/printflags/src/main.rs
index ae9b83a..7fcde61 100644
--- a/tools/aconfig/printflags/src/main.rs
+++ b/tools/aconfig/printflags/src/main.rs
@@ -16,8 +16,8 @@
 
 //! `printflags` is a device binary to print feature flags.
 
-use aconfig_protos::aconfig::Flag_state as State;
-use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
+use aconfig_protos::ProtoFlagState as State;
+use aconfig_protos::ProtoParsedFlags as ProtoParsedFlags;
 use anyhow::{bail, Context, Result};
 use regex::Regex;
 use std::collections::BTreeMap;