diff --git a/src/app.rs b/src/app.rs index 0c71158..8d251f4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -55,6 +55,11 @@ enum Subcommand { }, /// List available build modes List, + /// Add a new package to the project (throguh pkg-config) + Add { + /// The package to be added + package: String, + }, } #[derive(clap::Subcommand)] @@ -168,10 +173,69 @@ impl App { std::process::exit(1); } } + Subcommand::Add { package } => { + if let Err(e) = add_package(&package) { + eprintln!("Error adding package {package} to project: {e}"); + std::process::exit(1); + } + } } } } +fn add_package(package: &str) -> std::io::Result<()> { + let status = Command::new("pkg-config") + .arg("--exists") + .arg(&package) + .status() + .map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "pkg-config not found - please install it first", + ) + })?; + + if !status.success() { + eprintln!( + " {} pkg-config could not find package '{package}'", + "Error".red().bold() + ); + eprintln!( + " {} try installing the development package for '{package}', e.g.:", + "Hint".yellow().bold() + ); + eprintln!(" Arch: sudo pacman -S {package}"); + eprintln!(" Debian: sudo apt install lib{package}-dev"); + std::process::exit(1); + } + + let mut conf = get_config().ok_or(std::io::Error::new( + std::io::ErrorKind::NotFound, + "no Pallet.toml found in local directory", + ))?; + + let deps = conf.dependencies.get_or_insert_with(Vec::new); + + if deps.contains(&package.to_owned()) { + println!( + " {} '{package}' is already a dependency", + "Warning".yellow().bold() + ); + return Ok(()); + } + + deps.push(package.to_owned()); + + std::fs::write( + "Pallet.toml", + toml::to_string_pretty(&conf).expect("valid TOML"), + )?; + + println!(" {} {package}", "Added".green().bold()); + + Ok(()) +} + fn gen_compile_commands(mode: &Option) -> std::io::Result<()> { let conf = get_config().ok_or(std::io::Error::new( std::io::ErrorKind::NotFound, @@ -314,6 +378,50 @@ fn build(mode: &Option, force_recompile: bool) -> std::io::Result<()> { let compiler = conf.compiler.as_deref().unwrap_or("gcc"); + let mut extra_compile_flags: Vec = Vec::new(); + let mut extra_link_flags: Vec = Vec::new(); + + if let Some(deps) = &conf.dependencies { + if !deps.is_empty() { + let cflags = Command::new("pkg-config") + .arg("--cflags") + .args(deps) + .output() + .map_err(|_| { + std::io::Error::new(std::io::ErrorKind::NotFound, "pkg-config not found") + })?; + if !cflags.status.success() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "pkg-config --cflags failed - is the package installed?", + )); + } + + let libs = Command::new("pkg-config") + .arg("--libs") + .args(deps) + .output() + .map_err(|_| { + std::io::Error::new(std::io::ErrorKind::NotFound, "pkg-config not found") + })?; + if !libs.status.success() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "pkg-config --libs failed - is the pacakge installed?", + )); + } + + extra_compile_flags = String::from_utf8_lossy(&cflags.stdout) + .split_whitespace() + .map(str::to_owned) + .collect(); + extra_link_flags = String::from_utf8_lossy(&libs.stdout) + .split_whitespace() + .map(str::to_owned) + .collect(); + } + } + let source_files: Vec = glob("src/*.c") .map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}")))? .filter_map(|e| e.ok()) @@ -339,6 +447,7 @@ fn build(mode: &Option, force_recompile: bool) -> std::io::Result<()> { let status = Command::new(compiler) .args(&build_config.args) + .args(&extra_compile_flags) .arg("-c") .arg(src) .arg("-o") @@ -378,6 +487,7 @@ fn build(mode: &Option, force_recompile: bool) -> std::io::Result<()> { let status = Command::new(compiler) .args(&obj_files) + .args(&extra_link_flags) .arg("-o") .arg(&binary) .status()?; diff --git a/src/config.rs b/src/config.rs index 00fe400..f2421a8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,8 @@ pub struct Config { pub authors: Option>, /// Build configs pub build: Vec, + /// Build dependnecies verified by pkg-config + pub dependencies: Option>, } impl Config {