// Copyright (C) 2023 The Android Open Source Project // // 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. use std::env::current_dir; use std::fs::{create_dir_all, remove_dir_all}; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus, Output}; use std::str::from_utf8; use anyhow::{anyhow, Context, Result}; use semver::Version; use thiserror::Error; mod android_bp; mod crate_collection; mod crate_type; mod crates_io; mod license; mod managed_crate; mod managed_repo; mod patch; mod pseudo_crate; mod upgradable; pub use self::android_bp::maybe_build_cargo_embargo; pub use self::managed_repo::ManagedRepo; #[derive(Error, Debug)] pub enum CrateError { #[error("Virtual crate: {0}")] VirtualCrate(PathBuf), #[error("Duplicate crate version: {0} {1}")] DuplicateCrateVersion(String, Version), } pub fn default_repo_root() -> Result { let cwd = current_dir().context("Could not get current working directory")?; for cur in cwd.ancestors() { for e in cur.read_dir()? { if e?.file_name() == ".repo" { return Ok(cur.to_path_buf()); } } } Err(anyhow!(".repo directory not found in any ancestor of {}", cwd.display())) } pub fn ensure_exists_and_empty(dir: impl AsRef) -> Result<()> { let dir = dir.as_ref(); if dir.exists() { remove_dir_all(dir).context(format!("Failed to remove {}", dir.display()))?; } create_dir_all(dir).context(format!("Failed to create {}", dir.display())) } pub trait RunQuiet { fn run_quiet_and_expect_success(&mut self) -> Result; } impl RunQuiet for Command { fn run_quiet_and_expect_success(&mut self) -> Result { self.output() .context(format!("Failed to run {:?}", self))? .success_or_error() .context(format!("Failed to run {:?}", self)) } } pub trait SuccessOrError { fn success_or_error(self) -> Result where Self: std::marker::Sized; } impl SuccessOrError for ExitStatus { fn success_or_error(self) -> Result { if !self.success() { let exit_code = self.code().map(|code| format!("{}", code)).unwrap_or("(unknown)".to_string()); Err(anyhow!("Process failed with exit code {}", exit_code)) } else { Ok(self) } } } impl SuccessOrError for Output { fn success_or_error(self) -> Result { (&self).success_or_error()?; Ok(self) } } impl SuccessOrError for &Output { fn success_or_error(self) -> Result { if !self.status.success() { let exit_code = self .status .code() .map(|code| format!("{}", code)) .unwrap_or("(unknown)".to_string()); Err(anyhow!( "Process failed with exit code {}\nstdout:\n{}\nstderr:\n{}", exit_code, from_utf8(&self.stdout)?, from_utf8(&self.stderr)? )) } else { Ok(self) } } } // The copy_dir crate doesn't handle symlinks. pub fn copy_dir(src: impl AsRef, dst: impl AsRef) -> Result<()> { Command::new("cp") .arg("--archive") .arg(src.as_ref()) .arg(dst.as_ref()) .run_quiet_and_expect_success()?; Ok(()) }