• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! `record-finalized-flags` is a tool to create a snapshot (intended to be stored in
18 //! prebuilts/sdk) of the flags used with @FlaggedApi APIs
19 use anyhow::Result;
20 use clap::Parser;
21 use std::{collections::HashSet, fs::File, path::PathBuf};
22 
23 mod api_signature_files;
24 mod finalized_flags;
25 mod flag_values;
26 
27 pub(crate) type FlagId = String;
28 
29 const ABOUT: &str = "Create a new prebuilts/sdk/<version>/finalized-flags.txt file
30 
31 The prebuilts/sdk/<version>/finalized-flags.txt files list all aconfig flags that have been used
32 with @FlaggedApi annotations on APIs that have been finalized. These files are used to prevent
33 flags from being re-used for new, unfinalized, APIs, and by the aconfig code generation.
34 
35 This tool works as follows:
36 
37   - Read API signature files from source tree (*current.txt files) [--api-signature-file]
38   - Read the current aconfig flag values from source tree [--parsed-flags-file]
39   - Read the previous finalized-flags.txt files from prebuilts/sdk [--finalized-flags-file]
40   - Extract the flags slated for API finalization by scanning through the API signature files for
41     flags that are ENABLED and READ_ONLY
42   - Merge the found flags with the recorded flags from previous API finalizations
43   - Print the set of flags to stdout
44 ";
45 
46 #[derive(Parser, Debug)]
47 #[clap(about=ABOUT)]
48 struct Cli {
49     #[arg(long)]
50     parsed_flags_file: PathBuf,
51 
52     #[arg(long)]
53     api_signature_file: Vec<PathBuf>,
54 
55     #[arg(long)]
56     finalized_flags_file: PathBuf,
57 }
58 
59 /// Filter out the ENABLED and READ_ONLY flags used with @FlaggedApi annotations in the source
60 /// tree, and add those flags to the set of previously finalized flags.
calculate_new_finalized_flags( flags_used_with_flaggedapi_annotation: &HashSet<FlagId>, all_flags_to_be_finalized: &HashSet<FlagId>, already_finalized_flags: &HashSet<FlagId>, ) -> HashSet<FlagId>61 fn calculate_new_finalized_flags(
62     flags_used_with_flaggedapi_annotation: &HashSet<FlagId>,
63     all_flags_to_be_finalized: &HashSet<FlagId>,
64     already_finalized_flags: &HashSet<FlagId>,
65 ) -> HashSet<FlagId> {
66     let new_flags: HashSet<_> = flags_used_with_flaggedapi_annotation
67         .intersection(all_flags_to_be_finalized)
68         .map(|s| s.to_owned())
69         .collect();
70     already_finalized_flags.union(&new_flags).map(|s| s.to_owned()).collect()
71 }
72 
main() -> Result<()>73 fn main() -> Result<()> {
74     let args = Cli::parse();
75 
76     let mut flags_used_with_flaggedapi_annotation = HashSet::new();
77     for path in args.api_signature_file {
78         let file = File::open(path)?;
79         for flag in api_signature_files::extract_flagged_api_flags(file)?.drain() {
80             flags_used_with_flaggedapi_annotation.insert(flag);
81         }
82     }
83 
84     let file = File::open(args.parsed_flags_file)?;
85     let all_flags_to_be_finalized = flag_values::get_relevant_flags_from_binary_proto(file)?;
86 
87     let file = File::open(args.finalized_flags_file)?;
88     let already_finalized_flags = finalized_flags::read_finalized_flags(file)?;
89 
90     let mut new_finalized_flags = Vec::from_iter(calculate_new_finalized_flags(
91         &flags_used_with_flaggedapi_annotation,
92         &all_flags_to_be_finalized,
93         &already_finalized_flags,
94     ));
95     new_finalized_flags.sort();
96 
97     println!("{}", new_finalized_flags.join("\n"));
98 
99     Ok(())
100 }
101 
102 #[cfg(test)]
103 mod tests {
104     use super::*;
105 
106     #[test]
test()107     fn test() {
108         let input = include_bytes!("../tests/api-signature-file.txt");
109         let flags_used_with_flaggedapi_annotation =
110             api_signature_files::extract_flagged_api_flags(&input[..]).unwrap();
111 
112         let input = include_bytes!("../tests/flags.protobuf");
113         let all_flags_to_be_finalized =
114             flag_values::get_relevant_flags_from_binary_proto(&input[..]).unwrap();
115 
116         let input = include_bytes!("../tests/finalized-flags.txt");
117         let already_finalized_flags = finalized_flags::read_finalized_flags(&input[..]).unwrap();
118 
119         let new_finalized_flags = calculate_new_finalized_flags(
120             &flags_used_with_flaggedapi_annotation,
121             &all_flags_to_be_finalized,
122             &already_finalized_flags,
123         );
124 
125         assert_eq!(
126             new_finalized_flags,
127             HashSet::from_iter(vec![
128                 "record_finalized_flags.test.foo".to_string(),
129                 "record_finalized_flags.test.bar".to_string(),
130                 "record_finalized_flags.test.baz".to_string(),
131             ])
132         );
133     }
134 }
135