use std::{
collections::BTreeSet,
env, fs,
path::{Path, PathBuf},
process::Command,
};
use toml::Value;
fn main() {
let cli_manifest_dir = PathBuf::from(
env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should always be set"),
);
let authored_manifest = cli_manifest_dir.join("Cargo.toml.orig");
let manifest_path = if authored_manifest.exists() {
authored_manifest
} else {
cli_manifest_dir.join("Cargo.toml")
};
let manifest = manifest_value(&manifest_path);
let scaffold_metadata = &manifest["package"]["metadata"]["waterui-scaffold"];
if let Some(table) = scaffold_metadata.as_table() {
for key in table.keys() {
assert!(
key == "android-kotlin-version",
"Cargo.toml [package.metadata.waterui-scaffold].{key} is \
framework-owned: declare it in the water-rs/waterui root \
manifest's [package.metadata.waterui] table instead"
);
}
}
let kotlin_version = manifest_scaffold_string(scaffold_metadata, "android-kotlin-version");
println!("cargo:rustc-env=WATERUI_CLI_ANDROID_KOTLIN_VERSION={kotlin_version}");
println!(
"cargo:rustc-env=WATERUI_FRAMEWORK_REPOSITORY={}",
framework_repository(&manifest)
);
let cli_commit =
git(&cli_manifest_dir, &["rev-parse", "HEAD"]).unwrap_or_else(|| "unknown".to_string());
println!("cargo:rustc-env=WATERUI_CLI_COMMIT={cli_commit}");
println!("cargo:rerun-if-changed={}", manifest_path.display());
register_git_head_rerun(&cli_manifest_dir);
}
fn git(repo_root: &Path, args: &[&str]) -> Option<String> {
Command::new("git")
.current_dir(repo_root)
.args(args)
.output()
.ok()
.filter(|output| output.status.success())
.and_then(|output| String::from_utf8(output.stdout).ok())
.map(|output| output.trim().to_string())
.filter(|output| !output.is_empty())
}
fn register_git_head_rerun(repo_root: &Path) {
let Some(git_dir) = git(repo_root, &["rev-parse", "--git-dir"]) else {
return;
};
let git_dir_path = PathBuf::from(git_dir);
let git_dir_path = if git_dir_path.is_absolute() {
git_dir_path
} else {
repo_root.join(git_dir_path)
};
println!(
"cargo:rerun-if-changed={}",
git_dir_path.join("HEAD").display()
);
println!(
"cargo:rerun-if-changed={}",
git_dir_path.join("refs").display()
);
}
fn framework_repository(manifest: &Value) -> String {
let mut sources: BTreeSet<(String, String)> = BTreeSet::new();
for table in ["dependencies", "dev-dependencies", "build-dependencies"] {
let Some(dependencies) = manifest[table].as_table() else {
continue;
};
for (name, dependency) in dependencies {
if !name.starts_with("waterui-") {
continue;
}
let git = dependency
.get("git")
.and_then(Value::as_str)
.unwrap_or_else(|| panic!("Cargo.toml [{table}] {name} must be a git dependency"));
let rev = dependency
.get("rev")
.and_then(Value::as_str)
.unwrap_or_else(|| panic!("Cargo.toml [{table}] {name} must pin a rev"));
sources.insert((git.to_owned(), rev.to_owned()));
}
}
let mut sources = sources.into_iter();
let (git, _) = sources
.next()
.expect("Cargo.toml declares no waterui-* dependency");
if let Some((other_git, other_rev)) = sources.next() {
panic!(
"Cargo.toml pins waterui-* dependencies on more than one source: {git} and \
{other_git}@{other_rev} — move every waterui-* dependency to one git + rev"
);
}
git
}
fn manifest_scaffold_string(scaffold_metadata: &Value, key: &str) -> String {
scaffold_metadata[key]
.as_str()
.unwrap_or_else(|| panic!("missing package.metadata.waterui-scaffold.{key}"))
.to_string()
}
fn manifest_value(path: &Path) -> Value {
let contents = fs::read_to_string(path)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
toml::from_str::<Value>(&contents)
.unwrap_or_else(|error| panic!("failed to parse {}: {error}", path.display()))
}