diff options
| author | Namilskyy <alive6863@gmail.com> | 2025-12-06 16:36:44 +0300 |
|---|---|---|
| committer | Namilskyy <alive6863@gmail.com> | 2025-12-06 16:36:44 +0300 |
| commit | def46ae74c3f5974ed448e9877b0e0067a8e67d2 (patch) | |
| tree | 4eda9fe8b6da96ea8f1824a14235286fe6e49c72 /src/pkgtoolkit/build.rs | |
| parent | 79c8ecb6bf4d2fc2df5c90007e7c26b456ddc33f (diff) | |
Big code-cleaning in pkgtoolkit, implemented much functions and fixed logical mistakes
Diffstat (limited to 'src/pkgtoolkit/build.rs')
| -rw-r--r-- | src/pkgtoolkit/build.rs | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/pkgtoolkit/build.rs b/src/pkgtoolkit/build.rs new file mode 100644 index 0000000..a323521 --- /dev/null +++ b/src/pkgtoolkit/build.rs @@ -0,0 +1,349 @@ +use crate::cfg::config::Config; +use std::{fs::create_dir_all, path::Path, process::Command}; + +use num_cpus; + +use super::archive::ArchiveOperations; +use super::types::{Build, BuildSystems, Package}; + +pub trait BuildOperations { + fn execute_build(&self, build_meta: &Build) -> Result<(), std::io::Error>; + fn build(&mut self) -> Result<bool, std::io::Error>; +} + +#[allow(dead_code)] +impl BuildOperations for Package { + /// Execute the build script for the package. + /// + /// This function takes the `Build` meta information as an argument and + /// executes the build script accordingly. It also handles the different + /// build systems supported (Make, CMake, Meson, Cargo). + /// + /// # Errors + /// + /// Returns an error if the build command fails. + fn execute_build(&self, build_meta: &Build) -> Result<(), std::io::Error> { + let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?; + let build_dir = + Path::new(&config.paths.cache_dir).join(format!("{}-{}", self.name, self.version)); + // Check if build directory exists + if !build_dir.exists() { + return Err(std::io::Error::other(format!( + "Build directory not found: {}", + build_dir.display() + ))); + } + + // Prepare environment variables + let mut cmd_envs: Vec<(String, String)> = Vec::new(); + if let Some(ref env_vars) = build_meta.env { + for env_line in env_vars.lines() { + if let Some((key, value)) = env_line.split_once('=') { + cmd_envs.push((key.trim().to_string(), value.trim().to_string())); + } + } + } + + // Handle custom build script if provided + if let Some(ref script) = build_meta.script { + log::info!("Executing custom build script: {}", script); + Self::validate_custom_script(script)?; + let mut cmd = if script.starts_with("./") || script.contains('/') { + // Assume it's a file path + let script_path = build_dir.join(script); + if !script_path.exists() { + return Err(std::io::Error::other(format!( + "Custom script file not found: {}", + script_path.display() + ))); + } + let mut inner_cmd = Command::new("/bin/sh"); + inner_cmd.arg("-c"); + inner_cmd.arg(script_path.to_str().unwrap()); + inner_cmd + } else { + // Inline script + let mut inner_cmd = Command::new("/bin/sh"); + inner_cmd.arg("-c"); + inner_cmd.arg(script); + inner_cmd + }; + cmd.current_dir(&build_dir); + for (key, value) in &cmd_envs { + cmd.env(key, value); + } + let output = cmd + .output() + .map_err(|e| std::io::Error::other(format!("Custom build script failed: {}", e)))?; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + return Err(std::io::Error::other(format!( + "Custom build script failed:\nStdout: {}\nStderr: {}", + stdout, stderr + ))); + } + log::info!( + "Custom build script completed successfully for package: {}", + self.name + ); + return Ok(()); + } + + // No custom script, proceed with build system + match build_meta.build_system { + BuildSystems::Make => { + // Check for Makefile + let makefile_path = build_dir.join("Makefile"); + if !makefile_path.exists() { + return Err(std::io::Error::other(format!( + "Makefile not found: {}", + makefile_path.display() + ))); + } + let mut cmd = Command::new("make"); + cmd.current_dir(&build_dir); + cmd.arg("all"); + for (key, value) in &cmd_envs { + cmd.env(key, value); + } + log::info!("Running Make build: {:?}", cmd); + let output = cmd + .output() + .map_err(|e| std::io::Error::other(format!("Make build failed: {}", e)))?; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + return Err(std::io::Error::other(format!( + "Make build failed:\nStdout: {}\nStderr: {}", + stdout, stderr + ))); + } + } + BuildSystems::CMake => { + // Check for CMakeLists.txt + let cmake_file = build_dir.join("CMakeLists.txt"); + if !cmake_file.exists() { + return Err(std::io::Error::other(format!( + "CMakeLists.txt not found: {}", + cmake_file.display() + ))); + } + let build_dir_build = build_dir.join("build"); + create_dir_all(&build_dir_build)?; + let mut config_cmd = Command::new("cmake"); + config_cmd + .arg("-S") + .arg(&build_dir) + .arg("-B") + .arg(&build_dir_build) + .current_dir(&build_dir); + for (key, value) in &cmd_envs { + config_cmd.env(key, value); + } + log::info!("Running CMake configuration: {:?}", config_cmd); + let config_output = config_cmd + .output() + .map_err(|e| std::io::Error::other(format!("CMake config failed: {}", e)))?; + if !config_output.status.success() { + let stderr = String::from_utf8_lossy(&config_output.stderr); + return Err(std::io::Error::other(format!( + "CMake config failed:\n{}", + stderr + ))); + } + + // Now build + let mut build_cmd = Command::new("make"); + build_cmd.current_dir(&build_dir_build); + build_cmd.arg("-j").arg(num_cpus::get().to_string()); // Parallel build + for (key, value) in &cmd_envs { + build_cmd.env(key, value); + } + log::info!("Running CMake build: {:?}", build_cmd); + let build_output = build_cmd + .output() + .map_err(|e| std::io::Error::other(format!("CMake build failed: {}", e)))?; + if !build_output.status.success() { + let stderr = String::from_utf8_lossy(&build_output.stderr); + let stdout = String::from_utf8_lossy(&build_output.stdout); + return Err(std::io::Error::other(format!( + "CMake build failed:\nStdout: {}\nStderr: {}", + stdout, stderr + ))); + } + } + BuildSystems::Meson => { + // Check for meson.build + let meson_file = build_dir.join("meson.build"); + if !meson_file.exists() { + return Err(std::io::Error::other(format!( + "meson.build not found: {}", + meson_file.display() + ))); + } + let build_dir_build = build_dir.join("build"); + create_dir_all(&build_dir_build)?; + let mut config_cmd = Command::new("meson"); + config_cmd + .arg("setup") + .arg(&build_dir_build) + .current_dir(&build_dir); + for (key, value) in &cmd_envs { + config_cmd.env(key, value); + } + log::info!("Running Meson configuration: {:?}", config_cmd); + let config_output = config_cmd + .output() + .map_err(|e| std::io::Error::other(format!("Meson config failed: {}", e)))?; + if !config_output.status.success() { + let stderr = String::from_utf8_lossy(&config_output.stderr); + return Err(std::io::Error::other(format!( + "Meson config failed:\n{}", + stderr + ))); + } + + // Now build + let mut build_cmd = Command::new("ninja"); + build_cmd.current_dir(&build_dir_build); + build_cmd.arg("-j").arg(num_cpus::get().to_string()); // Parallel build + for (key, value) in &cmd_envs { + build_cmd.env(key, value); + } + log::info!("Running Meson build: {:?}", build_cmd); + let build_output = build_cmd + .output() + .map_err(|e| std::io::Error::other(format!("Meson build failed: {}", e)))?; + if !build_output.status.success() { + let stderr = String::from_utf8_lossy(&build_output.stderr); + let stdout = String::from_utf8_lossy(&build_output.stdout); + return Err(std::io::Error::other(format!( + "Meson build failed:\nStdout: {}\nStderr: {}", + stdout, stderr + ))); + } + } + BuildSystems::Cargo => { + // Check for Cargo.toml + let cargo_file = build_dir.join("Cargo.toml"); + if !cargo_file.exists() { + return Err(std::io::Error::other(format!( + "Cargo.toml not found: {}", + cargo_file.display() + ))); + } + let mut cmd = Command::new("cargo"); + cmd.arg("build").arg("--release").current_dir(&build_dir); + for (key, value) in &cmd_envs { + cmd.env(key, value); + } + log::info!("Running Cargo build: {:?}", cmd); + let output = cmd + .output() + .map_err(|e| std::io::Error::other(format!("Cargo build failed: {}", e)))?; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + return Err(std::io::Error::other(format!( + "Cargo build failed:\nStdout: {}\nStderr: {}", + stdout, stderr + ))); + } + } + } + + log::info!("Build completed successfully for package: {}", self.name); + Ok(()) + } + + /// Builds the package according to the BUILD file in the archive. + /// + /// There are two strategies for building the package. If the BUILD file is empty, the package is assumed to be a binary package and the default install hook is skipped. If the BUILD file is not empty, the package is assumed to be a source package and the default build hook is skipped. + /// + /// If the BUILD file is empty and the INSTALL file contains a custom script, the custom script is run instead of the default install hook. + /// If the BUILD file is not empty and the INSTALL file contains a custom script, the custom script is ignored. + /// + /// + /// # Errors + /// + /// Returns an error if the BUILD file is invalid or if the build or install hook fails. + fn build(&mut self) -> Result<bool, std::io::Error> { + let meta = Self::loadmeta(self)?; + let install_meta = meta.0; + // let setts_meta = meta.1; + let build_meta = meta.2; + + // BUILD NOT EMPTY. SOURCE: -> BUILD -> INSTALL -> SETTS + // BUILD EMPTY. BIN: -> INSTALL -> SETTS + enum Strategies { + Bin, + Source, + } + + let strategy; //default + + if build_meta.is_none() { + log::info!("BUILD file is empty. Skipping build, preparing to install"); + strategy = Strategies::Bin; + } else { + strategy = Strategies::Source; + log::info!("BUILD file is not empty. Skipping install, preparing to build"); + } + + match strategy { + Strategies::Bin => { + if install_meta.install.custom_script.is_none() { + log::info!("Strategy: BIN; No custom script. Running default install hook."); + } else { + log::info!("Strategy: BIN; Running custom script."); + let script = install_meta.install.custom_script.as_ref().unwrap(); + + // Validate script before execution + Self::validate_custom_script(script)?; + + if !script.starts_with("./") { + let output = std::process::Command::new(script).output().map_err(|e| { + std::io::Error::other(format!("Failed to execute custom script: {}", e)) + })?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(std::io::Error::other(format!( + "Custom script failed:\n{}", + stderr + ))); + } + } else { + let output = std::process::Command::new("/bin/sh") + .arg("-c") + .arg(script) + .output() + .map_err(|e| { + std::io::Error::other(format!( + "Failed to execute custom script: {}", + e + )) + })?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(std::io::Error::other(format!( + "Custom script failed:\n{}", + stderr + ))); + } + } + } + } + Strategies::Source => { + log::info!("Strategy: SOURCE; Running default build hook."); + if let Err(e) = self.execute_build(&build_meta.unwrap()) { + return Err(std::io::Error::other(format!("Build failed: {}", e))); + } + } + } + + Ok(true) + } +} |
