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