* Copyright (c) 2024 Huawei Technologies Co., Ltd.
* upatch-build is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
use std::ffi::OsString;
use std::path::PathBuf;
use anyhow::{ensure, Context, Result};
use clap::{AppSettings, ColorChoice, Parser};
use syscare_common::{fs, os};
use which::which;
use super::{CLI_ABOUT, CLI_NAME, CLI_VERSION};
const DEFAULT_EMPTY_VALUE: &str = "";
const DEFAULT_SOURCE_EXTS: &[&str] = &["h", "hpp", "hxx", "c", "cpp", "cxx", "in", "inc"];
const DEFAULT_COMPILERS: &[&str] = &["gcc", "g++"];
const DEFAULT_BUILD_ROOT: &str = ".";
const DEFAULT_OUTPUT_DIR: &str = ".";
#[derive(Parser, Debug)]
#[clap(
bin_name = CLI_NAME,
version = CLI_VERSION,
about = CLI_ABOUT,
arg_required_else_help(true),
color(ColorChoice::Never),
global_setting(AppSettings::DeriveDisplayOrder),
term_width(120),
)]
pub struct Arguments {
#[clap(long, default_value = DEFAULT_EMPTY_VALUE)]
pub prefix: OsString,
#[clap(long, default_value = DEFAULT_BUILD_ROOT)]
pub build_root: PathBuf,
#[clap(short, long)]
pub source_dir: PathBuf,
#[clap(long, multiple = true, default_values = &DEFAULT_SOURCE_EXTS)]
pub source_ext: Vec<OsString>,
#[clap(long, multiple = true, default_values = &DEFAULT_COMPILERS)]
pub compiler: Vec<PathBuf>,
#[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)]
pub prepare_cmd: OsString,
#[clap(short('c'), long)]
pub build_cmd: OsString,
#[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)]
pub clean_cmd: OsString,
#[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)]
pub object_dir: PathBuf,
#[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)]
pub binary_dir: PathBuf,
#[clap(short, long, multiple = true, required = true)]
pub binary: Vec<OsString>,
#[clap(short, long, multiple = true, required = true)]
pub debuginfo: Vec<PathBuf>,
#[clap(short, long, multiple = true, required = true)]
pub patch: Vec<PathBuf>,
#[clap(short, long, default_value = DEFAULT_OUTPUT_DIR)]
pub output_dir: PathBuf,
#[clap(long)]
pub override_line_macros: bool,
#[clap(long)]
pub skip_compiler_check: bool,
#[clap(long)]
pub skip_cleanup: bool,
#[clap(short, long)]
pub verbose: bool,
}
impl Arguments {
pub fn new() -> Result<Self> {
let mut args = Self::parse();
args.apply_defaults()
.setup_build_root()
.normalize()?
.check()?;
Ok(args)
}
fn setup_build_root(&mut self) -> &mut Self {
self.build_root = self
.build_root
.join(format!("upatch-build.{}", os::process::id()));
self
}
fn apply_defaults(&mut self) -> &mut Self {
if self.object_dir.as_os_str().is_empty() {
self.object_dir = self.source_dir.clone();
}
if self.binary_dir.as_os_str().is_empty() {
self.binary_dir = self.source_dir.clone();
}
self
}
fn normalize(&mut self) -> Result<&mut Self> {
self.build_root = fs::normalize(&self.build_root)?;
self.source_dir = fs::normalize(&self.source_dir)?;
self.object_dir = fs::normalize(&self.object_dir)?;
self.binary_dir = fs::normalize(&self.binary_dir)?;
self.output_dir = fs::normalize(&self.output_dir)?;
for compiler in &mut self.compiler {
*compiler = which(&compiler).with_context(|| {
format!("Cannot find compiler executable {}", compiler.display())
})?;
}
for debuginfo in &mut self.debuginfo {
*debuginfo = fs::normalize(&debuginfo)?;
}
for patch in &mut self.patch {
*patch = fs::normalize(&patch)?;
}
Ok(self)
}
fn check(&self) -> Result<()> {
ensure!(
self.source_dir.is_dir(),
format!("Cannot find source directory {}", self.source_dir.display())
);
for compiler in &self.compiler {
ensure!(
compiler.is_file(),
format!("Cannot find compiler {}", compiler.display())
);
}
for debuginfo in &self.debuginfo {
ensure!(
debuginfo.is_file(),
format!("Cannot find debuginfo {}", debuginfo.display())
);
}
ensure!(
self.binary.len() == self.debuginfo.len(),
"Cannot match the debuginfo corresponds to binary files"
);
for patch in &self.patch {
ensure!(
patch.is_file(),
format!("Cannot find patch {}", patch.display())
);
}
Ok(())
}
}
impl std::fmt::Display for Arguments {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:?}", self))
}
}