blob: eb316b2ac68c77124f92fb2ca6da8b1c2ab8b80b [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Main build script for the betosync project, used for both local development and in CI.
//!
//! Usage:
//! ```sh
//! cargo run -p build_scripts -- run-default-checks
//! ```
#![allow(missing_docs)]
use clap::Parser as _;
use cmd_runner::{
cargo_workspace::{CargoOptions, CargoWorkspaceSubcommand, FormatterOptions},
fuzzers::run_workspace_fuzz_targets,
license_checker::LicenseSubcommand,
};
use env_logger::Env;
use license::LICENSE_CHECKER;
use log::info;
use std::{env, path};
use xshell::{cmd, Shell};
mod license;
fn main() -> anyhow::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let cli: Cli = Cli::parse();
let build_script_dir = path::PathBuf::from(
env::var("CARGO_MANIFEST_DIR").expect("Must be run via Cargo to establish root directory"),
);
let root_dir = build_script_dir
.parent()
.expect("Parent dir from build_script crate should exist");
match cli.subcommand {
Subcommand::RunDefaultChecks(ref check_options) => {
run_default_checks(root_dir, check_options)?;
print!(concat!(
"Congratulations, the default checks passed. Since you like quality, here are\n",
"some more checks you may like:\n",
" cargo run -p build_scripts -- run-rust-fuzzers\n",
));
}
Subcommand::VerifyCi { ref check_options } => verify_ci(root_dir, check_options)?,
Subcommand::RunAllChecks { ref check_options } => run_all_checks(root_dir, check_options)?,
Subcommand::CleanEverything => clean_everything(root_dir)?,
Subcommand::CargoWorkspace(subcommand) => {
let sh = xshell::Shell::new()?;
sh.change_dir(root_dir);
subcommand.run(&sh, "betosync", ["--features=enable-required-features"])?
}
Subcommand::RunRustFuzzers => run_workspace_fuzz_targets(root_dir)?,
Subcommand::License(license_subcommand) => {
license_subcommand.run(&LICENSE_CHECKER, root_dir)?
}
}
Ok(())
}
fn check_java(sh: &Shell) -> anyhow::Result<()> {
let _guard = sh.push_dir("submerge/submerge_java");
cmd!(sh, "./gradlew :lib:test --rerun-tasks").run()?;
Ok(())
}
#[allow(clippy::if_same_then_else)]
pub fn verify_ci(root: &path::Path, check_options: &crate::CheckOptions) -> anyhow::Result<()> {
if cfg!(target_os = "linux") {
run_default_checks(root, check_options)?;
} else if cfg!(target_os = "macos") {
license::LICENSE_CHECKER.check(root)?;
let sh = xshell::Shell::new()?;
sh.change_dir(root);
check_options.cargo_options.check_workspace(
&sh,
"betosync",
["--features=enable-required-features"],
)?;
} else if cfg!(windows) {
license::LICENSE_CHECKER.check(root)?;
let sh = xshell::Shell::new()?;
sh.change_dir(root);
check_options.cargo_options.check_workspace(
&sh,
"betosync",
["--features=enable-required-features"],
)?;
} else {
anyhow::bail!("Unsupported OS for running CI!")
}
Ok(())
}
/// Runs checks to ensure lints are passing and all targets are building
pub fn run_all_checks(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
run_default_checks(root, check_options)?;
run_workspace_fuzz_targets(root)?;
Ok(())
}
/// Runs default checks that are suitable for verifying a local change.
pub fn run_default_checks(root: &path::Path, check_options: &CheckOptions) -> anyhow::Result<()> {
license::LICENSE_CHECKER.check(root)?;
let sh = xshell::Shell::new()?;
sh.change_dir(root);
check_options.cargo_options.check_workspace(
&sh,
"betosync",
["--features=enable-required-features"],
)?;
check_java(&sh)?;
Ok(())
}
pub fn clean_everything(root: &path::Path) -> anyhow::Result<()> {
let sh = Shell::new()?;
sh.change_dir(root);
cmd!(sh, "cargo clean").run()?;
sh.change_dir(root.join("submerge/submerge_java"));
cmd!(sh, "./gradlew :tests --rerun-tasks").run()?;
Ok(())
}
#[derive(clap::Parser)]
struct Cli {
#[clap(subcommand)]
subcommand: Subcommand,
}
#[derive(clap::Subcommand, Debug, Clone)]
enum Subcommand {
/// Runs all the checks that CI runs
VerifyCi {
#[command(flatten)]
check_options: CheckOptions,
},
/// Runs all available checks, including ones that are not normally run on CI
RunAllChecks {
#[command(flatten)]
check_options: CheckOptions,
},
/// Runs the default set of checks suitable for verifying local changes.
RunDefaultChecks(CheckOptions),
/// Cleans the main workspace and all sub projects - useful if upgrading rust compiler version
/// and need dependencies to be compiled with the same version
CleanEverything,
#[command(flatten)]
CargoWorkspace(CargoWorkspaceSubcommand),
/// Build and run pure Rust fuzzers for 10000 runs
RunRustFuzzers,
#[command(flatten)]
License(LicenseSubcommand),
}
#[derive(clap::Args, Debug, Clone, Default)]
pub struct CheckOptions {
#[command(flatten)]
formatter_options: FormatterOptions,
#[command(flatten)]
cargo_options: CargoOptions,
}