diff options
Diffstat (limited to 'llvm_tools/patch_sync/src/main.rs')
-rw-r--r-- | llvm_tools/patch_sync/src/main.rs | 253 |
1 files changed, 206 insertions, 47 deletions
diff --git a/llvm_tools/patch_sync/src/main.rs b/llvm_tools/patch_sync/src/main.rs index 081ce01a..c244f1c0 100644 --- a/llvm_tools/patch_sync/src/main.rs +++ b/llvm_tools/patch_sync/src/main.rs @@ -1,63 +1,110 @@ +mod android_utils; mod patch_parsing; mod version_control; +use std::borrow::ToOwned; +use std::collections::BTreeSet; +use std::path::{Path, PathBuf}; + use anyhow::{Context, Result}; -use std::path::PathBuf; use structopt::StructOpt; +use patch_parsing::{filter_patches_by_platform, PatchCollection, PatchDictSchema}; +use version_control::RepoSetupContext; + fn main() -> Result<()> { match Opt::from_args() { Opt::Show { cros_checkout_path, android_checkout_path, sync, - } => show_subcmd(cros_checkout_path, android_checkout_path, sync), + keep_unmerged, + } => show_subcmd(ShowOpt { + cros_checkout_path, + android_checkout_path, + sync, + keep_unmerged, + }), Opt::Transpose { cros_checkout_path, + cros_reviewers, old_cros_ref, android_checkout_path, + android_reviewers, old_android_ref, sync, verbose, dry_run, no_commit, + wip, + disable_cq, } => transpose_subcmd(TransposeOpt { cros_checkout_path, + cros_reviewers: cros_reviewers + .map(|r| r.split(',').map(ToOwned::to_owned).collect()) + .unwrap_or_default(), old_cros_ref, android_checkout_path, + android_reviewers: android_reviewers + .map(|r| r.split(',').map(ToOwned::to_owned).collect()) + .unwrap_or_default(), old_android_ref, sync, verbose, dry_run, no_commit, + wip, + disable_cq, }), } } -fn show_subcmd( +struct ShowOpt { cros_checkout_path: PathBuf, android_checkout_path: PathBuf, + keep_unmerged: bool, sync: bool, -) -> Result<()> { - let ctx = version_control::RepoSetupContext { +} + +fn show_subcmd(args: ShowOpt) -> Result<()> { + let ShowOpt { + cros_checkout_path, + android_checkout_path, + keep_unmerged, + sync, + } = args; + let ctx = RepoSetupContext { cros_checkout: cros_checkout_path, android_checkout: android_checkout_path, sync_before: sync, + wip_mode: true, // Has no effect, as we're not making changes + enable_cq: false, // Has no effect, as we're not uploading anything }; ctx.setup()?; - let cros_patches_path = ctx.cros_patches_path(); - let android_patches_path = ctx.android_patches_path(); - let cur_cros_collection = patch_parsing::PatchCollection::parse_from_file(&cros_patches_path) - .context("could not parse cros PATCHES.json")?; - let cur_android_collection = - patch_parsing::PatchCollection::parse_from_file(&android_patches_path) - .context("could not parse android PATCHES.json")?; + let make_collection = |platform: &str, patches_fp: &Path| -> Result<PatchCollection> { + let parsed_collection = PatchCollection::parse_from_file(patches_fp) + .with_context(|| format!("could not parse {} PATCHES.json", platform))?; + Ok(if keep_unmerged { + parsed_collection + } else { + filter_patches_by_platform(&parsed_collection, platform).map_patches(|p| { + // Need to do this platforms creation as Rust 1.55 cannot use "from". + let mut platforms = BTreeSet::new(); + platforms.insert(platform.to_string()); + PatchDictSchema { + platforms, + ..p.clone() + } + }) + }) + }; + let cur_cros_collection = make_collection("chromiumos", &ctx.cros_patches_path())?; + let cur_android_collection = make_collection("android", &ctx.android_patches_path())?; let merged = cur_cros_collection.union(&cur_android_collection)?; println!("{}", merged.serialize_patches()?); Ok(()) } -#[allow(dead_code)] struct TransposeOpt { cros_checkout_path: PathBuf, old_cros_ref: String, @@ -67,59 +114,147 @@ struct TransposeOpt { verbose: bool, dry_run: bool, no_commit: bool, + cros_reviewers: Vec<String>, + android_reviewers: Vec<String>, + wip: bool, + disable_cq: bool, } fn transpose_subcmd(args: TransposeOpt) -> Result<()> { - let ctx = version_control::RepoSetupContext { + let ctx = RepoSetupContext { cros_checkout: args.cros_checkout_path, android_checkout: args.android_checkout_path, sync_before: args.sync, + wip_mode: args.wip, + enable_cq: !args.disable_cq, }; ctx.setup()?; let cros_patches_path = ctx.cros_patches_path(); let android_patches_path = ctx.android_patches_path(); - // Chromium OS Patches ---------------------------------------------------- - let mut cur_cros_collection = - patch_parsing::PatchCollection::parse_from_file(&cros_patches_path) - .context("parsing cros PATCHES.json")?; - let new_cros_patches: patch_parsing::PatchCollection = { - let cros_old_patches_json = ctx.old_cros_patch_contents(&args.old_cros_ref)?; - let old_cros_collection = patch_parsing::PatchCollection::parse_from_str( - cros_patches_path.parent().unwrap().to_path_buf(), - &cros_old_patches_json, - )?; - cur_cros_collection.subtract(&old_cros_collection)? - }; + // Get new Patches ------------------------------------------------------- + let (cur_cros_collection, new_cros_patches) = patch_parsing::new_patches( + &cros_patches_path, + &ctx.old_cros_patch_contents(&args.old_cros_ref)?, + "chromiumos", + ) + .context("finding new patches for chromiumos")?; + let (cur_android_collection, new_android_patches) = patch_parsing::new_patches( + &android_patches_path, + &ctx.old_android_patch_contents(&args.old_android_ref)?, + "android", + ) + .context("finding new patches for android")?; + + // Have to ignore patches that are already at the destination, even if + // the patches are new. + let new_cros_patches = new_cros_patches.subtract(&cur_android_collection)?; + let new_android_patches = new_android_patches.subtract(&cur_cros_collection)?; - // Android Patches ------------------------------------------------------- - let mut cur_android_collection = - patch_parsing::PatchCollection::parse_from_file(&android_patches_path) - .context("parsing android PATCHES.json")?; - let new_android_patches: patch_parsing::PatchCollection = { - let android_old_patches_json = ctx.old_android_patch_contents(&args.old_android_ref)?; - let old_android_collection = patch_parsing::PatchCollection::parse_from_str( - android_patches_path.parent().unwrap().to_path_buf(), - &android_old_patches_json, - )?; - cur_android_collection.subtract(&old_android_collection)? + // Need to do an extra filtering step for Android, as AOSP doesn't + // want patches outside of the start/end bounds. + let android_llvm_version: u64 = { + let android_llvm_version_str = + android_utils::get_android_llvm_version(&ctx.android_checkout)?; + android_llvm_version_str.parse::<u64>().with_context(|| { + format!( + "converting llvm version to u64: '{}'", + android_llvm_version_str + ) + })? }; + let new_android_patches = new_android_patches.filter_patches(|p| { + match (p.get_start_version(), p.get_end_version()) { + (Some(start), Some(end)) => start <= android_llvm_version && android_llvm_version < end, + (Some(start), None) => start <= android_llvm_version, + (None, Some(end)) => android_llvm_version < end, + (None, None) => true, + } + }); + + if args.verbose { + display_patches("New patches from Chromium OS", &new_cros_patches); + display_patches("New patches from Android", &new_android_patches); + } + + if args.dry_run { + println!("--dry-run specified; skipping modifications"); + return Ok(()); + } + + modify_repos( + &ctx, + args.no_commit, + ModifyOpt { + new_cros_patches, + cur_cros_collection, + cros_reviewers: args.cros_reviewers, + new_android_patches, + cur_android_collection, + android_reviewers: args.android_reviewers, + }, + ) +} +struct ModifyOpt { + new_cros_patches: PatchCollection, + cur_cros_collection: PatchCollection, + cros_reviewers: Vec<String>, + new_android_patches: PatchCollection, + cur_android_collection: PatchCollection, + android_reviewers: Vec<String>, +} + +fn modify_repos(ctx: &RepoSetupContext, no_commit: bool, opt: ModifyOpt) -> Result<()> { + // Cleanup on scope exit. + scopeguard::defer! { + ctx.cleanup(); + } // Transpose Patches ----------------------------------------------------- - new_cros_patches.transpose_write(&mut cur_cros_collection)?; - new_android_patches.transpose_write(&mut cur_android_collection)?; + let mut cur_android_collection = opt.cur_android_collection; + let mut cur_cros_collection = opt.cur_cros_collection; + if !opt.new_cros_patches.is_empty() { + opt.new_cros_patches + .transpose_write(&mut cur_android_collection)?; + } + if !opt.new_android_patches.is_empty() { + opt.new_android_patches + .transpose_write(&mut cur_cros_collection)?; + } - if !args.no_commit { + if no_commit { + println!("--no-commit specified; not committing or uploading"); return Ok(()); } // Commit and upload for review ------------------------------------------ - ctx.cros_repo_upload() - .context("uploading chromiumos changes")?; - ctx.android_repo_upload() - .context("uploading android changes")?; + // Note we want to check if the android patches are empty for CrOS, and + // vice versa. This is a little counterintuitive. + if !opt.new_android_patches.is_empty() { + ctx.cros_repo_upload(&opt.cros_reviewers) + .context("uploading chromiumos changes")?; + } + if !opt.new_cros_patches.is_empty() { + if let Err(e) = android_utils::sort_android_patches(&ctx.android_checkout) { + eprintln!( + "Couldn't sort Android patches; continuing. Caused by: {}", + e + ); + } + ctx.android_repo_upload(&opt.android_reviewers) + .context("uploading android changes")?; + } Ok(()) } +fn display_patches(prelude: &str, collection: &PatchCollection) { + println!("{}", prelude); + if collection.patches.is_empty() { + println!(" [No Patches]"); + return; + } + println!("{}", collection); +} + #[derive(Debug, structopt::StructOpt)] #[structopt(name = "patch_sync", about = "A pipeline for syncing the patch code")] enum Opt { @@ -130,6 +265,12 @@ enum Opt { cros_checkout_path: PathBuf, #[structopt(parse(from_os_str))] android_checkout_path: PathBuf, + + /// Keep a patch's platform field even if it's not merged at that platform. + #[structopt(long)] + keep_unmerged: bool, + + /// Run repo sync before transposing. #[structopt(short, long)] sync: bool, }, @@ -140,6 +281,11 @@ enum Opt { #[structopt(long = "cros-checkout", parse(from_os_str))] cros_checkout_path: PathBuf, + /// Emails to send review requests to during Chromium OS upload. + /// Comma separated. + #[structopt(long = "cros-rev")] + cros_reviewers: Option<String>, + /// Git ref (e.g. hash) for the ChromiumOS overlay to use as the base. #[structopt(long = "overlay-base-ref")] old_cros_ref: String, @@ -148,6 +294,11 @@ enum Opt { #[structopt(long = "aosp-checkout", parse(from_os_str))] android_checkout_path: PathBuf, + /// Emails to send review requests to during Android upload. + /// Comma separated. + #[structopt(long = "aosp-rev")] + android_reviewers: Option<String>, + /// Git ref (e.g. hash) for the llvm_android repo to use as the base. #[structopt(long = "aosp-base-ref")] old_android_ref: String, @@ -161,13 +312,21 @@ enum Opt { verbose: bool, /// Do not change any files. Useful in combination with `--verbose` - /// Implies `--no-commit` and `--no-upload`. + /// Implies `--no-commit`. #[structopt(long)] dry_run: bool, - /// Do not commit any changes made. - /// Implies `--no-upload`. + /// Do not commit or upload any changes made. #[structopt(long)] no_commit: bool, + + /// Upload and send things for review, but mark as WIP and send no + /// emails. + #[structopt(long)] + wip: bool, + + /// Don't run CQ if set. Only has an effect if uploading. + #[structopt(long)] + disable_cq: bool, }, } |