Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d4c6caf authored by Dennis Shen's avatar Dennis Shen Committed by Automerger Merge Worker
Browse files

Merge "aconfig: add create storage command" into main am: 728fe988

parents cab7e07d 728fe988
Loading
Loading
Loading
Loading
+23 −0
Original line number Original line Diff line number Diff line
@@ -23,6 +23,8 @@ use std::path::PathBuf;
use crate::codegen::cpp::generate_cpp_code;
use crate::codegen::cpp::generate_cpp_code;
use crate::codegen::java::generate_java_code;
use crate::codegen::java::generate_java_code;
use crate::codegen::rust::generate_rust_code;
use crate::codegen::rust::generate_rust_code;
use crate::storage::generate_storage_files;

use crate::protos::{
use crate::protos::{
    ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
    ParsedFlagExt, ProtoFlagMetadata, ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag,
    ProtoParsedFlags, ProtoTracepoint,
    ProtoParsedFlags, ProtoTracepoint,
@@ -217,6 +219,17 @@ pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Ou
    generate_rust_code(package, parsed_flags.parsed_flag.iter(), codegen_mode)
    generate_rust_code(package, parsed_flags.parsed_flag.iter(), codegen_mode)
}
}


pub fn create_storage(caches: Vec<Input>, container: &str) -> Result<Vec<OutputFile>> {
    let parsed_flags_vec: Vec<ProtoParsedFlags> = caches
        .into_iter()
        .map(|mut input| input.try_parse_flags())
        .collect::<Result<Vec<_>>>()?
        .into_iter()
        .filter(|pfs| find_unique_container(pfs) == Some(container))
        .collect();
    generate_storage_files(container, parsed_flags_vec.iter())
}

pub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {
pub fn create_device_config_defaults(mut input: Input) -> Result<Vec<u8>> {
    let parsed_flags = input.try_parse_flags()?;
    let parsed_flags = input.try_parse_flags()?;
    let mut output = Vec::new();
    let mut output = Vec::new();
@@ -339,6 +352,16 @@ fn find_unique_package(parsed_flags: &ProtoParsedFlags) -> Option<&str> {
    Some(package)
    Some(package)
}
}


fn find_unique_container(parsed_flags: &ProtoParsedFlags) -> Option<&str> {
    let Some(container) = parsed_flags.parsed_flag.first().map(|pf| pf.container()) else {
        return None;
    };
    if parsed_flags.parsed_flag.iter().any(|pf| pf.container() != container) {
        return None;
    }
    Some(container)
}

#[cfg(test)]
#[cfg(test)]
mod tests {
mod tests {
    use super::*;
    use super::*;
+22 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ use std::path::{Path, PathBuf};
mod codegen;
mod codegen;
mod commands;
mod commands;
mod protos;
mod protos;
mod storage;


#[cfg(test)]
#[cfg(test)]
mod test;
mod test;
@@ -108,6 +109,17 @@ fn cli() -> Command {
                .arg(Arg::new("dedup").long("dedup").num_args(0).action(ArgAction::SetTrue))
                .arg(Arg::new("dedup").long("dedup").num_args(0).action(ArgAction::SetTrue))
                .arg(Arg::new("out").long("out").default_value("-")),
                .arg(Arg::new("out").long("out").default_value("-")),
        )
        )
        .subcommand(
            Command::new("create-storage")
                .arg(
                    Arg::new("container")
                        .long("container")
                        .required(true)
                        .help("The target container for the generated storage file."),
                )
                .arg(Arg::new("cache").long("cache").required(true))
                .arg(Arg::new("out").long("out").required(true)),
        )
}
}


fn get_required_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Result<&'a T>
fn get_required_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Result<&'a T>
@@ -242,6 +254,16 @@ fn main() -> Result<()> {
            let path = get_required_arg::<String>(sub_matches, "out")?;
            let path = get_required_arg::<String>(sub_matches, "out")?;
            write_output_to_file_or_stdout(path, &output)?;
            write_output_to_file_or_stdout(path, &output)?;
        }
        }
        Some(("create-storage", sub_matches)) => {
            let cache = open_zero_or_more_files(sub_matches, "cache")?;
            let container = get_required_arg::<String>(sub_matches, "container")?;
            let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
            let generated_files = commands::create_storage(cache, container)
                .context("failed to create storage files")?;
            generated_files
                .iter()
                .try_for_each(|file| write_output_file_realtive_to_dir(&dir, file))?;
        }
        _ => unreachable!(),
        _ => unreachable!(),
    }
    }
    Ok(())
    Ok(())
+166 −0
Original line number Original line Diff line number Diff line
/*
 * 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 anyhow::Result;
use std::collections::{HashMap, HashSet};

use crate::commands::OutputFile;
use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};

pub struct FlagPackage<'a> {
    pub package_name: &'a str,
    pub package_id: u32,
    pub flag_names: HashSet<&'a str>,
    pub boolean_flags: Vec<&'a ProtoParsedFlag>,
    pub boolean_offset: u32,
}

impl<'a> FlagPackage<'a> {
    fn new(package_name: &'a str, package_id: u32) -> Self {
        FlagPackage {
            package_name,
            package_id,
            flag_names: HashSet::new(),
            boolean_flags: vec![],
            boolean_offset: 0,
        }
    }

    fn insert(&mut self, pf: &'a ProtoParsedFlag) {
        if self.flag_names.insert(pf.name()) {
            self.boolean_flags.push(pf);
        }
    }
}

pub fn group_flags_by_package<'a, I>(parsed_flags_vec_iter: I) -> Vec<FlagPackage<'a>>
where
    I: Iterator<Item = &'a ProtoParsedFlags>,
{
    // group flags by package
    let mut packages: Vec<FlagPackage<'a>> = Vec::new();
    let mut package_index: HashMap<&'a str, usize> = HashMap::new();
    for parsed_flags in parsed_flags_vec_iter {
        for parsed_flag in parsed_flags.parsed_flag.iter() {
            let index = *(package_index.entry(parsed_flag.package()).or_insert(packages.len()));
            if index == packages.len() {
                packages.push(FlagPackage::new(parsed_flag.package(), index as u32));
            }
            packages[index].insert(parsed_flag);
        }
    }

    // calculate package flag value start offset, in flag value file, each boolean
    // is stored as two bytes, the first byte will be the flag value. the second
    // byte is flag info byte, which is a bitmask to indicate the status of a flag
    let mut boolean_offset = 0;
    for p in packages.iter_mut() {
        p.boolean_offset = boolean_offset;
        boolean_offset += 2 * p.boolean_flags.len() as u32;
    }

    packages
}

pub fn generate_storage_files<'a, I>(
    _containser: &str,
    parsed_flags_vec_iter: I,
) -> Result<Vec<OutputFile>>
where
    I: Iterator<Item = &'a ProtoParsedFlags>,
{
    let _packages = group_flags_by_package(parsed_flags_vec_iter);
    Ok(vec![])
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::Input;

    pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> {
        let aconfig_files = [
            (
                "com.android.aconfig.storage.test_1",
                "storage_test_1_part_1.aconfig",
                include_bytes!("../../tests/storage_test_1_part_1.aconfig").as_slice(),
            ),
            (
                "com.android.aconfig.storage.test_1",
                "storage_test_1_part_2.aconfig",
                include_bytes!("../../tests/storage_test_1_part_2.aconfig").as_slice(),
            ),
            (
                "com.android.aconfig.storage.test_2",
                "storage_test_2.aconfig",
                include_bytes!("../../tests/storage_test_2.aconfig").as_slice(),
            ),
        ];

        aconfig_files
            .into_iter()
            .map(|(pkg, file, content)| {
                let bytes = crate::commands::parse_flags(
                    pkg,
                    Some("system"),
                    vec![Input {
                        source: format!("tests/{}", file).to_string(),
                        reader: Box::new(content),
                    }],
                    vec![],
                    crate::commands::DEFAULT_FLAG_PERMISSION,
                )
                .unwrap();
                crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
            })
            .collect()
    }

    #[test]
    fn test_flag_package() {
        let caches = parse_all_test_flags();
        let packages = group_flags_by_package(caches.iter());

        for pkg in packages.iter() {
            let pkg_name = pkg.package_name;
            assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());
            for pf in pkg.boolean_flags.iter() {
                assert!(pkg.flag_names.contains(pf.name()));
                assert_eq!(pf.package(), pkg_name);
            }
        }

        assert_eq!(packages.len(), 2);

        assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1");
        assert_eq!(packages[0].package_id, 0);
        assert_eq!(packages[0].flag_names.len(), 5);
        assert!(packages[0].flag_names.contains("enabled_rw"));
        assert!(packages[0].flag_names.contains("disabled_rw"));
        assert!(packages[0].flag_names.contains("enabled_ro"));
        assert!(packages[0].flag_names.contains("disabled_ro"));
        assert!(packages[0].flag_names.contains("enabled_fixed_ro"));
        assert_eq!(packages[0].boolean_offset, 0);

        assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
        assert_eq!(packages[1].package_id, 1);
        assert_eq!(packages[1].flag_names.len(), 3);
        assert!(packages[1].flag_names.contains("enabled_ro"));
        assert!(packages[1].flag_names.contains("disabled_ro"));
        assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
        assert_eq!(packages[1].boolean_offset, 10);
    }
}
+17 −0
Original line number Original line Diff line number Diff line
package: "com.android.aconfig.storage.test_1"
container: "system"

flag {
    name: "enabled_rw"
    namespace: "aconfig_test"
    description: "This flag is ENABLED + READ_WRITE"
    bug: ""
}

flag {
    name: "disabled_rw"
    namespace: "aconfig_test"
    description: "This flag is DISABLED + READ_WRITE"
    bug: "456"
    is_exported: true
}
+24 −0
Original line number Original line Diff line number Diff line
package: "com.android.aconfig.storage.test_1"
container: "system"

flag {
    name: "enabled_ro"
    namespace: "aconfig_test"
    description: "This flag is ENABLED + READ_ONLY"
    bug: "abc"
}

flag {
    name: "disabled_ro"
    namespace: "aconfig_test"
    description: "This flag is DISABLED + READ_ONLY"
    bug: "123"
}

flag {
    name: "enabled_fixed_ro"
    namespace: "aconfig_test"
    description: "This flag is fixed READ_ONLY + ENABLED"
    bug: ""
    is_fixed_read_only: true
}
Loading