feat: add support for Mercurial (hg) source handling in mod.rs

- Implemented `split_hg_url` to parse Mercurial URLs and revisions.
- Added `checkout_hg` function to clone Mercurial repositories.
- Integrated Mercurial handling into the `prepare_one` function to support resuming builds.
- Added tests for `split_hg_url` to ensure correct parsing of URLs and revisions.

---

feat: introduce legacy STARBUILD conversion support in starbuild.rs

- Created `ConvertedStarbuild` and `ParsedStarbuild` structs to manage conversion data.
- Implemented `convert_starbuild_file` to parse STARBUILD files and generate package specifications.
- Added functions to handle parsing of various STARBUILD constructs, including dependencies and build phases.
- Developed a build script generator for STARBUILD, supporting custom install functions.
- Included tests to validate conversion of single and multi-output STARBUILD files.
This commit is contained in:
2026-03-23 15:13:07 -05:00
parent 19da5b960c
commit fd7873995a
10 changed files with 1653 additions and 9 deletions
+65
View File
@@ -13,6 +13,7 @@ use sha2::{Digest, Sha256};
use std::fs;
use std::os::unix::fs as unix_fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use url::Url;
use walkdir::WalkDir;
@@ -418,6 +419,20 @@ fn prepare_one(
// Treat `<url>#<rev>` and bare `*.git` sources as git before local file://
// handling so `file://...repo.git#tag` resolves through the git checkout path.
if let Some((base, rev)) = split_hg_url(&url) {
let checkout_dir = build_dir.join(&extract_dir_name);
if checkout_dir.exists() && checkout_dir.join(".depot_state").exists() {
crate::log_info!(
"Resuming build in existing hg directory: {}",
checkout_dir.display()
);
return Ok(checkout_dir);
}
checkout_hg(&base, &rev, &checkout_dir)?;
hooks::post_extract(spec, source, &checkout_dir, cache_dir)?;
return Ok(checkout_dir);
}
if let Some((base, rev)) = split_git_url(&url) {
let checkout_dir = build_dir.join(&extract_dir_name);
if checkout_dir.exists() && checkout_dir.join(".depot_state").exists() {
@@ -556,6 +571,45 @@ fn split_git_url(url: &str) -> Option<(String, String)> {
None
}
fn split_hg_url(url: &str) -> Option<(String, String)> {
let rest = url.strip_prefix("hg+")?;
if let Some((base, rev)) = rest.split_once('#') {
let revision = if rev.trim().is_empty() {
"tip"
} else {
rev.trim()
};
return Some((base.to_string(), revision.to_string()));
}
Some((rest.to_string(), "tip".to_string()))
}
fn checkout_hg(url: &str, rev: &str, checkout_dir: &Path) -> Result<()> {
if checkout_dir.exists() {
fs::remove_dir_all(checkout_dir).with_context(|| {
format!(
"Failed to remove existing Mercurial checkout dir: {}",
checkout_dir.display()
)
})?;
}
crate::log_info!("Cloning Mercurial source {} @ {}...", url, rev);
let mut cmd = Command::new("hg");
cmd.arg("clone")
.arg("-u")
.arg(rev)
.arg(url)
.arg(checkout_dir);
cmd.env("PATH", crate::runtime_env::safe_script_path());
let status = crate::interrupts::command_status(&mut cmd)
.with_context(|| format!("Failed to run hg clone for {}", url))?;
if !status.success() {
bail!("Mercurial clone failed for {} @ {}", url, rev);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
@@ -755,6 +809,17 @@ mod tests {
assert_eq!(rev, "0123456789abcdef");
}
#[test]
fn split_hg_url_accepts_revision_and_default_tip() {
let (base, rev) = split_hg_url("hg+https://hg.example.test/repo#v1").unwrap();
assert_eq!(base, "https://hg.example.test/repo");
assert_eq!(rev, "v1");
let (base, rev) = split_hg_url("hg+https://hg.example.test/repo").unwrap();
assert_eq!(base, "https://hg.example.test/repo");
assert_eq!(rev, "tip");
}
#[test]
fn prepare_one_rejects_cherry_pick_for_non_git_sources() {
let tmp = tempfile::tempdir().unwrap();