summaryrefslogtreecommitdiff
path: root/src/pkgtoolkit
diff options
context:
space:
mode:
authorNamilskyy <alive6863@gmail.com>2025-12-19 19:50:04 +0300
committerNamilskyy <alive6863@gmail.com>2025-12-19 19:50:04 +0300
commit12528fcaf99763fc25df694d31208a6fa729d0a3 (patch)
tree9149551ac7b3675cb93fedf0987b5c76eb6ff197 /src/pkgtoolkit
parentc5cdc8efd1d976f1ce53b124b9cbb33f6f2af097 (diff)
Working on make autobuild more modular
Diffstat (limited to 'src/pkgtoolkit')
-rw-r--r--src/pkgtoolkit/build.rs335
1 files changed, 201 insertions, 134 deletions
diff --git a/src/pkgtoolkit/build.rs b/src/pkgtoolkit/build.rs
index 326be1e..a37cb05 100644
--- a/src/pkgtoolkit/build.rs
+++ b/src/pkgtoolkit/build.rs
@@ -1,6 +1,11 @@
use crate::cfg::config::Config;
-use std::{fs::create_dir_all, path::Path, process::Command};
+use std::{
+ fs::create_dir_all,
+ path::{Path, PathBuf},
+ process::Command,
+};
+use glob::glob;
use num_cpus;
use super::archive::ArchiveOperations;
@@ -9,10 +14,144 @@ 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>;
+ fn find_makefile(
+ &self,
+ build_meta: &Build,
+ search_dir: &Path,
+ ) -> std::io::Result<Option<PathBuf>>;
+ fn run_command(cmd: std::process::Command, context: &str) -> Result<(), std::io::Error>;
+
+ fn run_build_system(
+ source_dir: &Path,
+ required_file: &str,
+ configure_cmd: &[&str],
+ build_cmd: &[&str],
+ work_dir: &Path,
+ envs: &[(String, String)],
+ context: &str,
+ ) -> Result<(), std::io::Error>;
}
#[allow(dead_code)]
impl BuildOperations for Package {
+ /// Runs a command and checks if it was successful.
+ /// If the command fails, it returns an error with the command's
+ /// stdout and stderr.
+ ///
+ /// # Arguments
+ ///
+ /// * `cmd`: The command to run.
+ /// * `context`: A string to prefix the error message with if the command fails.
+ fn run_command(mut cmd: std::process::Command, context: &str) -> Result<(), std::io::Error> {
+ let output = cmd
+ .output()
+ .map_err(|e| std::io::Error::other(format!("{} failed: {}", context, 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!(
+ "{} failed:\nStdout: {}\nStderr: {}",
+ context, stdout, stderr
+ )));
+ }
+ Ok(())
+ }
+
+ /// Runs a build system given the following parameters:
+ ///
+ /// `source_dir`: The directory containing the source code
+ /// `required_file`: The file that must exist in the source directory
+ /// `configure_cmd`: The command to run for configure step
+ /// `build_cmd`: The command to run for build step
+ /// `work_dir`: The directory where the build process will take place
+ /// `envs`: A list of environment variables to set during the build process
+ /// `context`: A string context to provide for error messages
+ fn run_build_system(
+ source_dir: &Path,
+ required_file: &str,
+ configure_cmd: &[&str],
+ build_cmd: &[&str],
+ work_dir: &Path,
+ envs: &[(String, String)],
+ context: &str,
+ ) -> Result<(), std::io::Error> {
+ let required_path = source_dir.join(required_file);
+ if !required_path.exists() {
+ return Err(std::io::Error::other(format!(
+ "{} file not found: {}",
+ context,
+ required_path.display()
+ )));
+ }
+
+ if !work_dir.exists() {
+ create_dir_all(work_dir)?;
+ }
+
+ // Configure step
+ let mut config_cmd = std::process::Command::new(configure_cmd[0]);
+ config_cmd.args(&configure_cmd[1..]).current_dir(source_dir);
+ for (key, value) in envs {
+ config_cmd.env(key, value);
+ }
+ log::info!("Running {} configuration: {:?}", context, config_cmd);
+ Self::run_command(config_cmd, &format!("{} config", context))?;
+
+ // Build step
+ let mut build_cmd_inner = std::process::Command::new(build_cmd[0]);
+ build_cmd_inner.args(&build_cmd[1..]).current_dir(work_dir);
+ for (key, value) in envs {
+ build_cmd_inner.env(key, value);
+ }
+ log::info!("Running {} build: {:?}", context, build_cmd_inner);
+ Self::run_command(build_cmd_inner, &format!("{} build", context))?;
+
+ Ok(())
+ }
+
+
+ /// Finds the build system file (e.g. Makefile, meson.build, CMakeLists.txt, Cargo.toml)
+ /// based on the build system specified in the build metadata.
+ ///
+ /// # Arguments
+ ///
+ /// * `build_meta`: The build metadata containing the build system type.
+ /// * `search_dir`: The directory to search in for the build system file.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing an `Option<PathBuf>` which is `Some` if the build
+ /// system file is found, and `None` otherwise.
+ fn find_makefile(&self, build_meta: &Build, search_dir: &Path) -> std::io::Result<Option<PathBuf>> {
+ let (patterns, recursive) = match build_meta.build_system {
+ BuildSystems::Make => (vec!["Makefile", "makefile", "GNUmakefile"], false),
+ BuildSystems::Meson => (vec!["meson.build", "meson_options.txt"], true),
+ BuildSystems::CMake => (vec!["CMakeLists.txt"], true),
+ BuildSystems::Cargo => (vec!["Cargo.toml"], false),
+ _ => return Ok(None),
+ };
+
+ for pattern in &patterns {
+
+ let glob_pattern = if recursive {
+ search_dir.join("**").join(pattern).to_string_lossy().into_owned()
+ } else {
+ search_dir.join(pattern).to_string_lossy().into_owned()
+ };
+
+ let entries = glob(&glob_pattern)
+ .map_err(|e| std::io::Error::other(format!("Invalid glob pattern: {}", e)))?;
+
+ for entry in entries {
+ let path = entry.map_err(|e| std::io::Error::other(format!("Glob error: {}", e)))?;
+ return Ok(Some(path));
+ }
+ }
+
+ Ok(None)
+}
+
/// Execute the build script for the package.
///
/// This function takes the `Build` meta information as an argument and
@@ -56,17 +195,20 @@ impl BuildOperations for Package {
let mut inner_cmd = Command::new("/bin/sh");
inner_cmd.arg("-c");
inner_cmd.arg(script_path.to_str().unwrap());
+ let _ = inner_cmd.output();
inner_cmd
} else {
let mut inner_cmd = Command::new("/bin/sh");
inner_cmd.arg("-c");
inner_cmd.arg(script);
+ let _ = inner_cmd.output();
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)))?;
@@ -88,14 +230,20 @@ impl BuildOperations for Package {
// 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() {
+ let found = self
+ .find_makefile(&build_meta, &build_dir)
+ .map_err(|e| {
+ std::io::Error::other(format!("Failed to search for Makefile: {}", e))
+ })?
+ .ok_or_else(|| std::io::Error::other("Makefile not found"))?;
+
+ if !found.exists() {
return Err(std::io::Error::other(format!(
"Makefile not found: {}",
- makefile_path.display()
+ found.display()
)));
}
+
let mut cmd = Command::new("make");
cmd.current_dir(&build_dir);
cmd.arg("all");
@@ -103,145 +251,53 @@ impl BuildOperations for Package {
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
- )));
- }
+ Self::run_command(cmd, "Make build")?;
}
BuildSystems::CMake => {
- 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
- )));
- }
+ let found = self
+ .find_makefile(&build_meta, &build_dir)
+ .map_err(|e| {
+ std::io::Error::other(format!("Failed to search for CMakeLists: {}", e))
+ })?
+ .ok_or_else(|| std::io::Error::other("Makefile not found"))?;
- 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);
+ if !found.exists() {
return Err(std::io::Error::other(format!(
- "CMake build failed:\nStdout: {}\nStderr: {}",
- stdout, stderr
+ "Makefile not found: {}",
+ found.display()
)));
}
+ Self::run_build_system(
+ &build_dir,
+ "CMakeLists.txt",
+ &["cmake", "-S", ".", "-B", "build"],
+ &["make", "-j", &num_cpus::get().to_string()],
+ &build_dir.join("build"),
+ &cmd_envs,
+ "CMake",
+ )?;
}
BuildSystems::Meson => {
- 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
- )));
- }
+ Self::run_build_system(
+ &build_dir,
+ "meson.build",
+ &["meson", "setup", "build"],
+ &["ninja", "-j", &num_cpus::get().to_string()],
+ &build_dir.join("build"),
+ &cmd_envs,
+ "Meson",
+ )?;
}
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
- )));
- }
+ Self::run_build_system(
+ &build_dir,
+ "Cargo.toml",
+ &["cargo", "build", "--release"],
+ &[],
+ &build_dir,
+ &cmd_envs,
+ "Cargo",
+ )?;
}
}
@@ -294,6 +350,16 @@ impl BuildOperations for Package {
// Validate script before execution
Self::validate_custom_script(script)?;
+ if self.arch.as_str().to_lowercase()
+ != install_meta.package.arch.as_str().to_lowercase()
+ {
+ return Err(std::io::Error::other(format!(
+ "Package arch mismatch. Expected: {}, Actual: {}",
+ install_meta.package.arch.as_str(),
+ self.arch.as_str().to_lowercase()
+ )));
+ }
+
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))
@@ -328,6 +394,7 @@ impl BuildOperations for Package {
}
}
}
+
Strategies::Source => {
log::info!("Strategy: SOURCE; Running default build hook.");
if let Err(e) = self.execute_build(&build_meta.unwrap()) {