use std::path::{Path, PathBuf};
use anyhow::Context;
use ax_config_gen::{
GenerateOptions, OutputFormat, generate_config, load_config_specs, read_config_value,
read_loaded_config_string, read_loaded_config_value,
};
use clap::{Args, Subcommand};
use crate::build::{
resolve_platform_config_by_package, resolve_platform_config_by_package_with_metadata,
workspace_metadata,
};
#[derive(Subcommand)]
pub enum Command {
PlatformPath(PlatformPathArgs),
Read(ReadArgs),
Generate(GenerateArgs),
Inspect(InspectArgs),
}
#[derive(Args)]
pub struct PlatformPathArgs {
#[arg(long = "package", value_name = "PACKAGE")]
package: String,
}
#[derive(Args)]
pub struct ReadArgs {
#[arg(required = true, value_name = "SPEC")]
specs: Vec<PathBuf>,
#[arg(short, long, value_name = "ITEM")]
read: String,
}
#[derive(Args)]
pub struct GenerateArgs {
#[arg(required = true, value_name = "SPEC")]
specs: Vec<PathBuf>,
#[arg(short = 'c', long)]
oldconfig: Option<PathBuf>,
#[arg(short, long)]
output: PathBuf,
#[arg(short, long, value_name = "WR_CONFIG")]
write: Vec<String>,
}
#[derive(Args)]
pub struct InspectArgs {
#[arg(long = "package", value_name = "PACKAGE")]
package: String,
#[arg(long = "manifest-dir", value_name = "DIR")]
manifest_dir: Option<PathBuf>,
#[arg(long = "config", value_name = "PATH")]
config: Option<PathBuf>,
#[arg(long)]
makefile: bool,
}
pub fn execute(command: Command) -> anyhow::Result<()> {
match command {
Command::PlatformPath(args) => platform_path(args),
Command::Read(args) => read(args),
Command::Generate(args) => generate(args),
Command::Inspect(args) => inspect(args),
}
}
fn platform_path(args: PlatformPathArgs) -> anyhow::Result<()> {
let metadata = workspace_metadata().context("failed to load workspace metadata")?;
let platform = resolve_platform_config_by_package(&args.package, &metadata)?;
println!("{}", platform.config_path.display());
Ok(())
}
fn read(args: ReadArgs) -> anyhow::Result<()> {
let value = read_config_value(&args.specs, &args.read).with_context(|| {
format!(
"failed to read config item `{}` from {}",
args.read,
args.specs
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(", ")
)
})?;
println!("{value}");
Ok(())
}
fn generate(args: GenerateArgs) -> anyhow::Result<()> {
let report = generate_config(&GenerateOptions {
specs: args.specs,
oldconfig: args.oldconfig,
output: Some(args.output),
fmt: OutputFormat::Toml,
writes: args.write,
keep_backup: true,
})
.context("failed to generate config")?;
for item in report.untouched {
eprintln!(
"[WARN] config item `{}` not set in the old config, using default value",
item.item_name()
);
}
for item in report.extra {
eprintln!(
"[WARN] config item `{}` not found in the specification, ignoring",
item.item_name()
);
}
Ok(())
}
fn inspect(args: InspectArgs) -> anyhow::Result<()> {
let platform_config = match args.config {
Some(path) => path,
None => {
if let Some(manifest_dir) = args.manifest_dir.as_deref() {
resolve_platform_config_from_manifest_dir(&args.package, manifest_dir)?.config_path
} else {
let metadata = workspace_metadata().context("failed to load workspace metadata")?;
resolve_platform_config_by_package(&args.package, &metadata)?.config_path
}
}
};
let config = load_config_specs(std::slice::from_ref(&platform_config)).with_context(|| {
format!(
"failed to load platform config {}",
platform_config.display()
)
})?;
let package =
read_loaded_config_string(&config, "package").context("failed to read package")?;
let platform =
read_loaded_config_string(&config, "platform").context("failed to read platform")?;
let arch = read_loaded_config_string(&config, "arch").context("failed to read arch")?;
let max_cpu_num =
read_loaded_config_value(&config, "plat.max-cpu-num").unwrap_or_else(|_| String::new());
let phys_memory_size = read_loaded_config_value(&config, "plat.phys-memory-size")
.unwrap_or_else(|_| String::new());
let platform_config = platform_config.display().to_string();
if args.makefile {
println!(
"PLAT_CONFIG={} PLAT_PACKAGE={} PLAT_NAME={} PLAT_ARCH={} PLAT_SMP={} \
PHYS_MEMORY_SIZE={}",
platform_config, package, platform, arch, max_cpu_num, phys_memory_size
);
} else {
println!("PLAT_CONFIG={}", shell_escape(&platform_config));
println!("PLAT_PACKAGE={}", shell_escape(&package));
println!("PLAT_NAME={}", shell_escape(&platform));
println!("PLAT_ARCH={}", shell_escape(&arch));
println!("PLAT_SMP={max_cpu_num}");
println!("PHYS_MEMORY_SIZE={phys_memory_size}");
}
Ok(())
}
fn resolve_platform_config_from_manifest_dir(
package: &str,
manifest_dir: &Path,
) -> anyhow::Result<crate::build::ResolvedPlatformConfig> {
let manifest_path = manifest_dir.join("Cargo.toml");
let metadata = crate::context::workspace_metadata_root_manifest(&manifest_path)
.with_context(|| format!("failed to load metadata for {}", manifest_path.display()))?;
let deps_metadata = crate::context::workspace_metadata_root_manifest_with_deps(&manifest_path)
.with_context(|| {
format!(
"failed to load dependency metadata for {}",
manifest_path.display()
)
})?;
resolve_platform_config_by_package_with_metadata(package, &metadata, &deps_metadata)
}
fn shell_escape(value: &str) -> String {
if value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.' | b'/'))
{
value.to_string()
} else {
format!("'{}'", value.replace('\'', "'\\''"))
}
}