aconfig: follow Java conventions for Java file paths
Update codegen_java to write the generated Java file(s) to
"java/package/File.java" instead of just "File.java".
Also generalize codegen_java::GeneratedFile to commands::OutputFile in
preparation for the upcoming C++ and Rust codegen.
Also change Java package name to 'com.android.internal.aconfig'.
Bug: 279485059
Test: atest aconfig.test
Change-Id: I13978697e35010fe6be8637aa495d4b852dbed7e
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 08b762a..bbf1272 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -20,13 +20,9 @@
use crate::aconfig::{FlagState, Permission};
use crate::cache::{Cache, Item};
+use crate::commands::OutputFile;
-pub struct GeneratedFile {
- pub file_content: String,
- pub file_name: String,
-}
-
-pub fn generate_java_code(cache: &Cache) -> Result<GeneratedFile> {
+pub fn generate_java_code(cache: &Cache) -> Result<OutputFile> {
let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
let readwrite = class_elements.iter().any(|item| item.readwrite);
let namespace = uppercase_first_letter(
@@ -35,8 +31,9 @@
let context = Context { namespace: namespace.clone(), readwrite, class_elements };
let mut template = TinyTemplate::new();
template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
- let file_content = template.render("java_code_gen", &context)?;
- Ok(GeneratedFile { file_content, file_name: format!("{}.java", namespace) })
+ let contents = template.render("java_code_gen", &context)?;
+ let path = ["com", "android", "internal", "aconfig", &(namespace + ".java")].iter().collect();
+ Ok(OutputFile { contents: contents.into(), path })
}
#[derive(Serialize)]
@@ -123,7 +120,7 @@
},
)
.unwrap();
- let expect_content = "package com.android.aconfig;
+ let expect_content = r#"package com.android.internal.aconfig;
import android.provider.DeviceConfig;
@@ -135,17 +132,19 @@
public static boolean test2() {
return DeviceConfig.getBoolean(
- \"Testflag\",
- \"test2__test2\",
+ "Testflag",
+ "test2__test2",
false
);
}
}
- ";
- let expected_file_name = format!("{}.java", uppercase_first_letter(namespace));
- let generated_file = generate_java_code(&cache).unwrap();
- assert_eq!(expected_file_name, generated_file.file_name);
- assert_eq!(expect_content.replace(' ', ""), generated_file.file_content.replace(' ', ""));
+ "#;
+ let file = generate_java_code(&cache).unwrap();
+ assert_eq!("com/android/internal/aconfig/Testflag.java", file.path.to_str().unwrap());
+ assert_eq!(
+ expect_content.replace(' ', ""),
+ String::from_utf8(file.contents).unwrap().replace(' ', "")
+ );
}
}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index c02fe07..475d9b8 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -20,10 +20,11 @@
use serde::{Deserialize, Serialize};
use std::fmt;
use std::io::Read;
+use std::path::PathBuf;
use crate::aconfig::{FlagDeclarations, FlagValue};
use crate::cache::Cache;
-use crate::codegen_java::{generate_java_code, GeneratedFile};
+use crate::codegen_java::generate_java_code;
use crate::protos::ProtoParsedFlags;
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -47,6 +48,11 @@
pub reader: Box<dyn Read>,
}
+pub struct OutputFile {
+ pub path: PathBuf, // relative to some root directory only main knows about
+ pub contents: Vec<u8>,
+}
+
pub fn create_cache(
namespace: &str,
declarations: Vec<Input>,
@@ -85,7 +91,7 @@
Ok(cache)
}
-pub fn generate_code(cache: &Cache) -> Result<GeneratedFile> {
+pub fn generate_code(cache: &Cache) -> Result<OutputFile> {
generate_java_code(cache)
}
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 4abcb90..513e313 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -16,11 +16,12 @@
//! `aconfig` is a build time tool to manage build time configurations, such as feature flags.
-use anyhow::Result;
+use anyhow::{anyhow, ensure, Result};
use clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command};
use std::fs;
use std::io;
use std::io::Write;
+use std::path::{Path, PathBuf};
mod aconfig;
mod cache;
@@ -29,7 +30,7 @@
mod protos;
use crate::cache::Cache;
-use commands::{Input, Source};
+use commands::{Input, OutputFile, Source};
fn cli() -> Command {
Command::new("aconfig")
@@ -68,6 +69,22 @@
Ok(opened_files)
}
+fn write_output_file_realtive_to_dir(root: &Path, output_file: &OutputFile) -> Result<()> {
+ ensure!(
+ root.is_dir(),
+ "output directory {} does not exist or is not a directory",
+ root.display()
+ );
+ let path = root.join(output_file.path.clone());
+ let parent = path
+ .parent()
+ .ok_or(anyhow!("unable to locate parent of output file {}", path.display()))?;
+ fs::create_dir_all(parent)?;
+ let mut file = fs::File::create(path)?;
+ file.write_all(&output_file.contents)?;
+ Ok(())
+}
+
fn main() -> Result<()> {
let matches = cli().get_matches();
match matches.subcommand() {
@@ -84,12 +101,9 @@
let path = sub_matches.get_one::<String>("cache").unwrap();
let file = fs::File::open(path)?;
let cache = Cache::read_from_reader(file)?;
- let out = sub_matches.get_one::<String>("out").unwrap();
+ let dir = PathBuf::from(sub_matches.get_one::<String>("out").unwrap());
let generated_file = commands::generate_code(&cache).unwrap();
- fs::write(
- format!("{}/{}", out, generated_file.file_name),
- generated_file.file_content,
- )?;
+ write_output_file_realtive_to_dir(&dir, &generated_file)?;
}
Some(("dump", sub_matches)) => {
let path = sub_matches.get_one::<String>("cache").unwrap();
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
index 3854579..ebcd607 100644
--- a/tools/aconfig/templates/java.template
+++ b/tools/aconfig/templates/java.template
@@ -1,4 +1,4 @@
-package com.android.aconfig;
+package com.android.internal.aconfig;
{{ if readwrite }}
import android.provider.DeviceConfig;
{{ endif }}