use std::collections::{BTreeMap, HashMap, HashSet};
use std::fs;
use std::io::{self, BufRead, BufReader, Read, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
pub use crate::clang::clang_x86_pipeline::{
self, compile_to_x86_object, compile_with_options, CompilationCache, PipelineResult,
X86CompileOptions, X86OptLevel, X86OutputFormat, X86Pipeline,
};
use crate::clang::{
self, compile_c_file, compile_c_string, CLangStandard, ClangCodeGen, ClangDriver, ClangOptions,
DiagSeverity, DiagnosticBuilder, DiagnosticEngine, DiagnosticOptions,
};
use crate::x86::{
self, X86CallingConvention, X86InstrInfo, X86IsaFeature, X86RegisterInfo, X86Subtarget,
X86TargetMachine, X86_64_REG_COUNT, X86_PAGE_SIZE, X86_RED_ZONE_SIZE_64,
X86_STACK_ALIGNMENT_32, X86_STACK_ALIGNMENT_64,
};
pub const X86_64_LINUX_TRIPLE: &str = "x86_64-unknown-linux-gnu";
pub const X86_32_LINUX_TRIPLE: &str = "i686-unknown-linux-gnu";
pub const X86_64_WINDOWS_TRIPLE: &str = "x86_64-pc-windows-msvc";
pub const X86_64_DARWIN_TRIPLE: &str = "x86_64-apple-darwin";
pub const DEFAULT_PARALLEL_JOBS: usize = 0; pub const DEFAULT_CACHE_DIR: &str = ".x86-build-cache";
pub const DEFAULT_CACHE_MAX_SIZE: u64 = 1_073_741_824;
pub const X86_CPU_FEATURES: &[&str] = &[
"mmx", "sse", "sse2", "sse3", "ssse3", "sse4.1", "sse4.2", "avx", "avx2", "avx512f",
"avx512cd", "avx512bw", "avx512dq", "avx512vl", "fma", "bmi", "bmi2", "lzcnt", "popcnt",
"rdrand", "rdseed", "aes", "sha", "adx", "sgx", "cet",
];
pub const X86_MICROARCHS: &[&str] = &[
"pentium",
"pentium-mmx",
"pentiumpro",
"pentium2",
"pentium3",
"pentium4",
"prescott",
"nocona",
"core2",
"penryn",
"nehalem",
"westmere",
"sandybridge",
"ivybridge",
"haswell",
"broadwell",
"skylake",
"skylake-avx512",
"cannonlake",
"icelake-client",
"icelake-server",
"cascadelake",
"cooperlake",
"tigerlake",
"rocketlake",
"alderlake",
"raptorlake",
"sierraforest",
"graniterapids",
"meteorlake",
"arrowlake",
"lunarlake",
"znver1",
"znver2",
"znver3",
"znver4",
"znver5",
"x86-64",
"x86-64-v2",
"x86-64-v3",
"x86-64-v4",
];
pub struct X86RealProjectCompiler {
pub project_root: PathBuf,
pub build_dir: PathBuf,
pub target_triple: String,
pub target_cpu: String,
pub target_features: Vec<String>,
pub opt_level: X86OptLevel,
pub options: X86CompileOptions,
pub project_config: Option<X86ProjectConfig>,
pub build_results: Vec<X86BuildResult>,
pub test_results: Vec<X86TestSuiteResult>,
pub diagnostics: Vec<X86CompileDiagnostic>,
pub verbose: bool,
pub parallel_jobs: usize,
}
#[derive(Debug, Clone)]
pub struct X86ProjectConfig {
pub name: String,
pub build_system: X86BuildSystem,
pub detector: X86ProjectDetector,
pub configure: X86BuildConfiguration,
pub dependencies: X86DependencyMap,
pub sources: Vec<PathBuf>,
pub includes: Vec<PathBuf>,
pub lib_dirs: Vec<PathBuf>,
pub libraries: Vec<String>,
pub defines: Vec<(String, Option<String>)>,
pub compiler_flags: Vec<String>,
pub linker_flags: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86BuildResult {
pub file: PathBuf,
pub success: bool,
pub duration: Duration,
pub object_file: Option<PathBuf>,
pub errors: Vec<X86CompileDiagnostic>,
pub warnings: Vec<X86CompileDiagnostic>,
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
#[derive(Debug, Clone)]
pub struct X86CompileDiagnostic {
pub severity: X86DiagLevel,
pub message: String,
pub file: Option<PathBuf>,
pub line: Option<u32>,
pub column: Option<u32>,
pub code: Option<String>,
pub fixits: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86DiagLevel {
Note,
Warning,
Error,
Fatal,
}
impl X86DiagLevel {
pub fn as_str(&self) -> &'static str {
match self {
Self::Note => "note",
Self::Warning => "warning",
Self::Error => "error",
Self::Fatal => "fatal error",
}
}
}
impl std::fmt::Display for X86DiagLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl X86RealProjectCompiler {
pub fn new(project_root: &Path) -> Self {
let build_dir = project_root.join("build").join("x86-out");
Self {
project_root: project_root.to_path_buf(),
build_dir,
target_triple: X86_64_LINUX_TRIPLE.to_string(),
target_cpu: "x86-64".to_string(),
target_features: Vec::new(),
opt_level: X86OptLevel::O2,
options: X86CompileOptions::default(),
project_config: None,
build_results: Vec::new(),
test_results: Vec::new(),
diagnostics: Vec::new(),
verbose: false,
parallel_jobs: DEFAULT_PARALLEL_JOBS,
}
}
pub fn x86_64_linux(project_root: &Path) -> Self {
let mut compiler = Self::new(project_root);
compiler.target_triple = X86_64_LINUX_TRIPLE.to_string();
compiler.target_cpu = "x86-64".to_string();
compiler.options = X86CompileOptions::x86_64_linux();
compiler
}
pub fn x86_32_linux(project_root: &Path) -> Self {
let mut compiler = Self::new(project_root);
compiler.target_triple = X86_32_LINUX_TRIPLE.to_string();
compiler.target_cpu = "pentium4".to_string();
compiler.options = X86CompileOptions::x86_32_linux();
compiler
}
pub fn x86_64_windows(project_root: &Path) -> Self {
let mut compiler = Self::new(project_root);
compiler.target_triple = X86_64_WINDOWS_TRIPLE.to_string();
compiler.options = X86CompileOptions::x86_64_windows();
compiler
}
pub fn with_opt_level(mut self, level: X86OptLevel) -> Self {
self.opt_level = level;
self.options.opt_level = level;
self
}
pub fn with_verbose(mut self, verbose: bool) -> Self {
self.verbose = verbose;
self.options.verbose = verbose;
self
}
pub fn with_parallel_jobs(mut self, jobs: usize) -> Self {
self.parallel_jobs = jobs;
self
}
pub fn with_features(mut self, features: &[&str]) -> Self {
self.target_features = features.iter().map(|s| s.to_string()).collect();
self.options.target_features = features.iter().map(|s| s.to_string()).collect();
self
}
pub fn with_target_triple(mut self, triple: &str) -> Self {
self.target_triple = triple.to_string();
self.options.target_triple = triple.to_string();
self
}
pub fn with_target_cpu(mut self, cpu: &str) -> Self {
self.target_cpu = cpu.to_string();
self.options.target_cpu = cpu.to_string();
self
}
pub fn detect_project(&mut self) -> Result<&X86ProjectConfig, String> {
let detector = X86ProjectDetector::scan(&self.project_root);
let configure =
X86ConfigureDetector::detect(&self.project_root, &detector, &self.target_triple);
let dependencies =
X86DependencyResolver::resolve(&self.project_root, &detector, &configure);
let mut sources = detector.collect_sources();
sources.sort();
sources.dedup();
let mut includes = detector.collect_include_dirs();
includes.sort();
includes.dedup();
let compiler_flags = configure.all_compiler_flags();
let linker_flags = configure.all_linker_flags();
let config = X86ProjectConfig {
name: detector.project_name().to_string(),
build_system: detector.build_system,
detector: detector.clone(),
configure: configure.clone(),
dependencies,
sources,
includes,
lib_dirs: configure.lib_dirs.clone(),
libraries: configure.libraries.clone(),
defines: configure.defines.clone(),
compiler_flags,
linker_flags,
};
if self.verbose {
eprintln!(
"[X86RealProjectCompiler] Detected project '{}' with {} sources (build system: {})",
config.name,
config.sources.len(),
config.build_system.as_str(),
);
}
self.project_config = Some(config.clone());
Ok(self.project_config.as_ref().unwrap())
}
pub fn build(&mut self) -> Result<Vec<X86BuildResult>, String> {
if self.project_config.is_none() {
self.detect_project()?;
}
let config = self.project_config.as_ref().unwrap();
self.build_results.clear();
self.diagnostics.clear();
fs::create_dir_all(&self.build_dir)
.map_err(|e| format!("Cannot create build directory: {}", e))?;
let mut runner = X86BuildRunner::new(&self.build_dir, self.parallel_jobs, self.verbose);
let results = runner.compile_project(
&config.sources,
&config.includes,
&config.defines,
&config.compiler_flags,
&self.options,
)?;
for result in &results {
if !result.success {
self.diagnostics.extend(result.errors.clone());
}
self.diagnostics.extend(result.warnings.clone());
}
self.build_results = results.clone();
Ok(results)
}
pub fn test(&mut self) -> Result<Vec<X86TestSuiteResult>, String> {
if self.project_config.is_none() {
self.detect_project()?;
}
let config = self.project_config.as_ref().unwrap();
let mut runner = X86TestRunner::new(&self.build_dir, &self.project_root, self.verbose);
let results = runner.run_project_tests(&config.name, &config.build_system);
self.test_results = results.clone();
Ok(results)
}
pub fn build_and_test(&mut self) -> Result<X86ProjectOutcome, String> {
let build = self.build()?;
let tests = self.test()?;
let build_success = build.iter().all(|r| r.success);
let test_success = tests.iter().all(|t| t.all_passed());
Ok(X86ProjectOutcome {
project_name: self
.project_config
.as_ref()
.map(|c| c.name.clone())
.unwrap_or_default(),
build_success,
test_success,
build_results: build,
test_results: tests,
total_duration: self.build_results.iter().map(|r| r.duration).sum(),
})
}
pub fn generate_report(&self) -> String {
let mut report = String::new();
report.push_str("═══════════════════════════════════════════════════════════\n");
report.push_str(" X86 Real Project Compilation Report\n");
report.push_str("═══════════════════════════════════════════════════════════\n\n");
if let Some(ref config) = self.project_config {
report.push_str(&format!("Project: {}\n", config.name));
report.push_str(&format!(
"Build System: {}\n",
config.build_system.as_str()
));
report.push_str(&format!("Target Triple: {}\n", self.target_triple));
report.push_str(&format!("Target CPU: {}\n", self.target_cpu));
report.push_str(&format!("Opt Level: {:?}\n", self.opt_level));
report.push_str(&format!("Sources: {}\n", config.sources.len()));
report.push_str(&format!("Defines: {}\n", config.defines.len()));
report.push_str(&format!("Includes: {}\n", config.includes.len()));
report.push_str(&format!("Libraries: {}\n", config.libraries.len()));
report.push('\n');
}
let built = self.build_results.iter().filter(|r| r.success).count();
let failed = self.build_results.iter().filter(|r| !r.success).count();
let total_build = self.build_results.len();
report.push_str("── Build Results ─────────────────────────────────────────\n");
report.push_str(&format!(
" Files compiled: {} / {} ({} failed)\n",
built, total_build, failed,
));
let total_errors: usize = self.build_results.iter().map(|r| r.errors.len()).sum();
let total_warnings: usize = self.build_results.iter().map(|r| r.warnings.len()).sum();
report.push_str(&format!(" Errors: {}\n", total_errors));
report.push_str(&format!(" Warnings: {}\n", total_warnings));
report.push('\n');
if !self.test_results.is_empty() {
let test_passed: usize = self.test_results.iter().map(|t| t.passed).sum();
let test_failed: usize = self.test_results.iter().map(|t| t.failed).sum();
report.push_str("── Test Results ──────────────────────────────────────────\n");
report.push_str(&format!(" Passed: {}\n", test_passed));
report.push_str(&format!(" Failed: {}\n", test_failed));
report.push('\n');
}
if let Some(ref outcome) = self.last_outcome() {
report.push_str("── Final Status ──────────────────────────────────────────\n");
report.push_str(&format!(
" Build: {}\n",
if outcome.build_success {
"PASS"
} else {
"FAIL"
}
));
report.push_str(&format!(
" Tests: {}\n",
if outcome.test_success { "PASS" } else { "FAIL" }
));
report.push('\n');
}
report.push_str("═══════════════════════════════════════════════════════════\n");
report
}
fn last_outcome(&self) -> Option<X86ProjectOutcome> {
if self.build_results.is_empty() {
return None;
}
let build_success = self.build_results.iter().all(|r| r.success);
let test_success = self.test_results.iter().all(|t| t.all_passed());
Some(X86ProjectOutcome {
project_name: self
.project_config
.as_ref()
.map(|c| c.name.clone())
.unwrap_or_default(),
build_success,
test_success,
build_results: self.build_results.clone(),
test_results: self.test_results.clone(),
total_duration: self.build_results.iter().map(|r| r.duration).sum(),
})
}
pub fn error_count(&self) -> usize {
self.build_results.iter().map(|r| r.errors.len()).sum()
}
pub fn warning_count(&self) -> usize {
self.build_results.iter().map(|r| r.warnings.len()).sum()
}
pub fn build_succeeded(&self) -> bool {
!self.build_results.is_empty() && self.build_results.iter().all(|r| r.success)
}
}
#[derive(Debug, Clone)]
pub struct X86ProjectOutcome {
pub project_name: String,
pub build_success: bool,
pub test_success: bool,
pub build_results: Vec<X86BuildResult>,
pub test_results: Vec<X86TestSuiteResult>,
pub total_duration: Duration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86BuildSystem {
CMake,
Make,
Autotools,
Meson,
Bazel,
Cargo,
Ninja,
SCons,
Custom,
Unknown,
}
impl X86BuildSystem {
pub fn as_str(&self) -> &'static str {
match self {
Self::CMake => "cmake",
Self::Make => "make",
Self::Autotools => "autotools",
Self::Meson => "meson",
Self::Bazel => "bazel",
Self::Cargo => "cargo",
Self::Ninja => "ninja",
Self::SCons => "scons",
Self::Custom => "custom",
Self::Unknown => "unknown",
}
}
pub fn default_build_file(&self) -> Vec<&'static str> {
match self {
Self::CMake => vec!["CMakeLists.txt"],
Self::Make => vec!["Makefile", "makefile", "GNUmakefile"],
Self::Autotools => vec!["configure.ac", "configure.in", "Makefile.am"],
Self::Meson => vec!["meson.build"],
Self::Bazel => vec!["BUILD", "BUILD.bazel", "WORKSPACE"],
Self::Cargo => vec!["Cargo.toml"],
Self::Ninja => vec!["build.ninja"],
Self::SCons => vec!["SConstruct", "Sconstruct", "sconstruct"],
Self::Custom | Self::Unknown => vec![],
}
}
}
#[derive(Debug, Clone)]
pub struct X86ProjectDetector {
pub project_root: PathBuf,
pub build_system: X86BuildSystem,
pub all_files: Vec<PathBuf>,
pub source_groups: HashMap<PathBuf, Vec<PathBuf>>,
pub header_groups: HashMap<PathBuf, Vec<PathBuf>>,
pub variables: HashMap<String, String>,
pub targets: Vec<X86BuildTarget>,
pub sub_projects: Vec<X86ProjectDetector>,
pub special_files: HashMap<String, PathBuf>,
pub version: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86BuildTarget {
pub name: String,
pub kind: X86TargetKind,
pub sources: Vec<PathBuf>,
pub defines: Vec<(String, Option<String>)>,
pub includes: Vec<PathBuf>,
pub compiler_flags: Vec<String>,
pub linker_flags: Vec<String>,
pub dependencies: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TargetKind {
Executable,
StaticLibrary,
SharedLibrary,
HeaderOnly,
ObjectFile,
Custom,
}
impl X86TargetKind {
pub fn as_str(&self) -> &'static str {
match self {
Self::Executable => "executable",
Self::StaticLibrary => "static_library",
Self::SharedLibrary => "shared_library",
Self::HeaderOnly => "header_only",
Self::ObjectFile => "object",
Self::Custom => "custom",
}
}
}
impl X86ProjectDetector {
pub fn scan(project_root: &Path) -> Self {
let build_system = Self::detect_build_system(project_root);
let all_files = Self::collect_all_files(project_root);
let (source_groups, header_groups) = Self::group_sources(&all_files);
let variables = Self::parse_build_variables(project_root, build_system);
let targets = Self::discover_targets(project_root, build_system, &variables);
let special_files = Self::find_special_files(project_root);
let version = Self::detect_version(project_root, build_system, &variables);
let description = Self::detect_description(project_root, build_system, &variables);
let sub_projects = Self::discover_sub_projects(project_root);
Self {
project_root: project_root.to_path_buf(),
build_system,
all_files,
source_groups,
header_groups,
variables,
targets,
sub_projects,
special_files,
version,
description,
}
}
pub fn detect_build_system(root: &Path) -> X86BuildSystem {
if root.join("CMakeLists.txt").exists() {
return X86BuildSystem::CMake;
}
if root.join("meson.build").exists() {
return X86BuildSystem::Meson;
}
if root.join("BUILD.bazel").exists() || root.join("WORKSPACE").exists() {
return X86BuildSystem::Bazel;
}
if root.join("BUILD").exists() {
match fs::read_to_string(root.join("BUILD")) {
Ok(content) if content.contains("cc_library") || content.contains("cc_binary") => {
return X86BuildSystem::Bazel;
}
_ => {}
}
}
if root.join("Cargo.toml").exists()
&& (root.join("build.rs").exists() || Self::has_c_dependencies(root))
{
return X86BuildSystem::Cargo;
}
if root.join("SConstruct").exists() || root.join("Sconstruct").exists() {
return X86BuildSystem::SCons;
}
if root.join("build.ninja").exists() {
return X86BuildSystem::Ninja;
}
if root.join("configure.ac").exists()
|| root.join("configure.in").exists()
|| (root.join("configure").exists() && root.join("Makefile.am").exists())
{
return X86BuildSystem::Autotools;
}
if root.join("Makefile").exists()
|| root.join("makefile").exists()
|| root.join("GNUmakefile").exists()
{
return X86BuildSystem::Make;
}
if root.join("build.sh").exists()
|| root.join("build.bat").exists()
|| root.join("compile.sh").exists()
{
return X86BuildSystem::Custom;
}
X86BuildSystem::Unknown
}
fn has_c_dependencies(root: &Path) -> bool {
root.join("src").join("c").exists()
|| root.join("c_src").exists()
|| root.join("native").exists()
}
pub fn collect_all_files(root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
Self::walk_files(root, root, &mut files, 4); files
}
fn walk_files(base: &Path, current: &Path, files: &mut Vec<PathBuf>, max_depth: usize) {
if max_depth == 0 {
return;
}
if let Ok(entries) = fs::read_dir(current) {
for entry in entries.flatten() {
let path = entry.path();
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with('.') && name != "." && name != ".." {
if path.is_dir() {
continue;
}
}
if name == "build" || name == "target" || name == "node_modules" || name == ".git" {
continue;
}
if path.is_dir() {
Self::walk_files(base, &path, files, max_depth - 1);
} else if path.is_file() {
if let Ok(rel) = path.strip_prefix(base) {
files.push(rel.to_path_buf());
} else {
files.push(path.clone());
}
}
}
}
}
pub fn group_sources(
all_files: &[PathBuf],
) -> (
HashMap<PathBuf, Vec<PathBuf>>,
HashMap<PathBuf, Vec<PathBuf>>,
) {
let source_exts: HashSet<&str> = ["c", "cc", "cpp", "cxx", "c++", "C", "m", "mm", "s", "S"]
.iter()
.cloned()
.collect();
let header_exts: HashSet<&str> = ["h", "hh", "hpp", "hxx", "h++", "H", "inc", "inl"]
.iter()
.cloned()
.collect();
let mut sources: HashMap<PathBuf, Vec<PathBuf>> = HashMap::new();
let mut headers: HashMap<PathBuf, Vec<PathBuf>> = HashMap::new();
for file in all_files {
let ext = file.extension().and_then(|e| e.to_str()).unwrap_or("");
let dir = file.parent().unwrap_or(Path::new(".")).to_path_buf();
if source_exts.contains(ext) {
sources.entry(dir).or_default().push(file.clone());
} else if header_exts.contains(ext) {
headers.entry(dir).or_default().push(file.clone());
}
}
(sources, headers)
}
pub fn parse_build_variables(root: &Path, system: X86BuildSystem) -> HashMap<String, String> {
match system {
X86BuildSystem::CMake => Self::parse_cmake_variables(root),
X86BuildSystem::Make => Self::parse_makefile_variables(root),
X86BuildSystem::Autotools => Self::parse_autotools_variables(root),
X86BuildSystem::Meson => Self::parse_meson_variables(root),
X86BuildSystem::Bazel => Self::parse_bazel_variables(root),
X86BuildSystem::Cargo => Self::parse_cargo_variables(root),
_ => HashMap::new(),
}
}
fn parse_cmake_variables(root: &Path) -> HashMap<String, String> {
let mut vars = HashMap::new();
let cmake_path = root.join("CMakeLists.txt");
if let Ok(content) = fs::read_to_string(&cmake_path) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.to_lowercase().starts_with("project(") {
let inner = Self::extract_parens(trimmed, "project");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split_whitespace().collect();
if !parts.is_empty() {
vars.insert("PROJECT_NAME".to_string(), parts[0].to_string());
}
if let Some(vpos) = parts.iter().position(|p| p.to_uppercase() == "VERSION")
{
if vpos + 1 < parts.len() {
vars.insert(
"PROJECT_VERSION".to_string(),
parts[vpos + 1].to_string(),
);
}
}
}
}
if trimmed.to_lowercase().starts_with("set(") {
let inner = Self::extract_parens(trimmed, "set");
if let Some(inner) = inner {
let parts: Vec<&str> =
inner.splitn(2, |c: char| c.is_whitespace()).collect();
if parts.len() >= 2 {
vars.insert(parts[0].to_string(), parts[1].trim().to_string());
}
}
}
if trimmed.to_lowercase().starts_with("option(") {
let inner = Self::extract_parens(trimmed, "option");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split_whitespace().collect();
if parts.len() >= 3 {
vars.insert(parts[0].to_string(), parts[parts.len() - 1].to_string());
}
}
}
}
}
vars
}
fn parse_makefile_variables(root: &Path) -> HashMap<String, String> {
let mut vars = HashMap::new();
for name in &["Makefile", "makefile", "GNUmakefile"] {
let path = root.join(name);
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
if let Some(eq) = trimmed.find(|c| c == '=') {
let var_name = trimmed[..eq]
.trim()
.trim_end_matches(&[':', '+'] as &[_])
.trim();
let value = trimmed[eq + 1..].trim();
if !var_name.is_empty() && var_name != "ifeq" && var_name != "ifneq" {
vars.insert(var_name.to_string(), value.to_string());
}
}
}
break; }
}
vars
}
fn parse_autotools_variables(root: &Path) -> HashMap<String, String> {
let mut vars = HashMap::new();
for cfg_name in &["configure.ac", "configure.in"] {
let path = root.join(cfg_name);
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("AC_INIT(") {
let inner = Self::extract_parens(trimmed, "AC_INIT");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split(',').collect();
if !parts.is_empty() {
let name = parts[0].trim().trim_matches('[').trim_matches(']');
vars.insert("PACKAGE_NAME".to_string(), name.to_string());
}
if parts.len() >= 2 {
let version = parts[1].trim().trim_matches('[').trim_matches(']');
vars.insert("PACKAGE_VERSION".to_string(), version.to_string());
}
if parts.len() >= 3 {
let bugreport = parts[2].trim().trim_matches('[').trim_matches(']');
vars.insert("PACKAGE_BUGREPORT".to_string(), bugreport.to_string());
}
}
}
}
break;
}
}
vars
}
fn parse_meson_variables(root: &Path) -> HashMap<String, String> {
let mut vars = HashMap::new();
let path = root.join("meson.build");
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("project(") {
let inner = Self::extract_parens(trimmed, "project");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split(',').collect();
if !parts.is_empty() {
let name = parts[0].trim().trim_matches('\'').trim_matches('"');
vars.insert("PROJECT_NAME".to_string(), name.to_string());
}
if parts.len() >= 2 {
let version = parts[1].trim().trim_matches('\'').trim_matches('"');
vars.insert("PROJECT_VERSION".to_string(), version.to_string());
}
}
}
}
}
vars
}
fn parse_bazel_variables(root: &Path) -> HashMap<String, String> {
let mut vars = HashMap::new();
for build_file in &["BUILD.bazel", "BUILD", "WORKSPACE"] {
let path = root.join(build_file);
if let Ok(content) = fs::read_to_string(&path) {
if build_file == &"WORKSPACE" {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("workspace(name") {
if let Some(inner) = Self::extract_parens(trimmed, "workspace") {
let parts: Vec<&str> = inner.split('=').collect();
if parts.len() >= 2 {
let name = parts[1].trim().trim_matches('"');
vars.insert("WORKSPACE_NAME".to_string(), name.to_string());
}
}
}
}
}
break;
}
}
vars
}
fn parse_cargo_variables(root: &Path) -> HashMap<String, String> {
let mut vars = HashMap::new();
let path = root.join("Cargo.toml");
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("name") {
if let Some(val) = trimmed
.strip_prefix("name")
.and_then(|s| s.strip_prefix("="))
{
let name = val.trim().trim_matches('"').trim_matches('\'');
vars.insert("CARGO_PKG_NAME".to_string(), name.to_string());
}
}
if trimmed.starts_with("version") {
if let Some(val) = trimmed
.strip_prefix("version")
.and_then(|s| s.strip_prefix("="))
{
let version = val.trim().trim_matches('"').trim_matches('\'');
vars.insert("CARGO_PKG_VERSION".to_string(), version.to_string());
}
}
}
}
vars
}
pub fn discover_targets(
root: &Path,
system: X86BuildSystem,
variables: &HashMap<String, String>,
) -> Vec<X86BuildTarget> {
match system {
X86BuildSystem::CMake => Self::discover_cmake_targets(root),
X86BuildSystem::Make => Self::discover_make_targets(root),
X86BuildSystem::Autotools => Self::discover_autotools_targets(root),
X86BuildSystem::Meson => Self::discover_meson_targets(root),
X86BuildSystem::Bazel => Self::discover_bazel_targets(root),
_ => Vec::new(),
}
}
fn discover_cmake_targets(root: &Path) -> Vec<X86BuildTarget> {
let mut targets = Vec::new();
let path = root.join("CMakeLists.txt");
let content = match fs::read_to_string(&path) {
Ok(c) => c,
Err(_) => return targets,
};
for line in content.lines() {
let trimmed = line.trim().to_lowercase();
if trimmed.starts_with("add_executable(") {
let inner = Self::extract_parens(line.trim(), "add_executable");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split_whitespace().collect();
if !parts.is_empty() {
targets.push(X86BuildTarget {
name: parts[0].to_string(),
kind: X86TargetKind::Executable,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
} else if trimmed.starts_with("add_library(") {
let inner = Self::extract_parens(line.trim(), "add_library");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split_whitespace().collect();
if parts.len() >= 2 {
let kind = match parts[1].to_uppercase().as_str() {
"STATIC" => X86TargetKind::StaticLibrary,
"SHARED" => X86TargetKind::SharedLibrary,
"INTERFACE" => X86TargetKind::HeaderOnly,
_ => X86TargetKind::StaticLibrary,
};
targets.push(X86BuildTarget {
name: parts[0].to_string(),
kind,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
}
targets
}
fn discover_make_targets(root: &Path) -> Vec<X86BuildTarget> {
let mut targets = Vec::new();
for name in &["Makefile", "makefile", "GNUmakefile"] {
let path = root.join(name);
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
if trimmed.contains(':') && !trimmed.contains('=') {
if let Some(colon) = trimmed.find(':') {
let target_part = trimmed[..colon].trim();
if !target_part.contains('%')
&& !target_part.is_empty()
&& target_part != ".PHONY"
&& target_part != ".SUFFIXES"
&& target_part != ".DEFAULT"
&& target_part != ".INTERMEDIATE"
{
targets.push(X86BuildTarget {
name: target_part.to_string(),
kind: X86TargetKind::Custom,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
}
break;
}
}
targets
}
fn discover_autotools_targets(root: &Path) -> Vec<X86BuildTarget> {
let mut targets = Vec::new();
if let Ok(content) = fs::read_to_string(root.join("Makefile.am")) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("bin_PROGRAMS") {
if let Some(val) = trimmed.split('=').nth(1) {
for prog in val.split_whitespace() {
targets.push(X86BuildTarget {
name: prog.to_string(),
kind: X86TargetKind::Executable,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
if trimmed.starts_with("lib_LTLIBRARIES") || trimmed.starts_with("lib_LIBRARIES") {
if let Some(val) = trimmed.split('=').nth(1) {
for lib in val.split_whitespace() {
targets.push(X86BuildTarget {
name: lib.to_string(),
kind: X86TargetKind::SharedLibrary,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
}
}
targets
}
fn discover_meson_targets(root: &Path) -> Vec<X86BuildTarget> {
let mut targets = Vec::new();
if let Ok(content) = fs::read_to_string(root.join("meson.build")) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.contains("executable(") {
if let Some(start) = trimmed.find("executable(") {
let rest = &trimmed[start + 11..];
if let Some(end) = rest.find(',') {
let name = rest[..end].trim().trim_matches('\'').trim_matches('"');
targets.push(X86BuildTarget {
name: name.to_string(),
kind: X86TargetKind::Executable,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
if trimmed.contains("library(")
|| trimmed.contains("shared_library(")
|| trimmed.contains("static_library(")
{
let (prefix, kind) = if trimmed.contains("shared_library(") {
("shared_library(", X86TargetKind::SharedLibrary)
} else if trimmed.contains("static_library(") {
("static_library(", X86TargetKind::StaticLibrary)
} else {
("library(", X86TargetKind::StaticLibrary)
};
if let Some(start) = trimmed.find(prefix) {
let rest = &trimmed[start + prefix.len()..];
if let Some(end) = rest.find(',') {
let name = rest[..end].trim().trim_matches('\'').trim_matches('"');
targets.push(X86BuildTarget {
name: name.to_string(),
kind,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
}
}
targets
}
fn discover_bazel_targets(root: &Path) -> Vec<X86BuildTarget> {
let mut targets = Vec::new();
for build_file in &["BUILD", "BUILD.bazel"] {
let path = root.join(build_file);
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
let (prefix, kind) = if trimmed.starts_with("cc_binary(") {
("cc_binary(", X86TargetKind::Executable)
} else if trimmed.starts_with("cc_library(") {
("cc_library(", X86TargetKind::StaticLibrary)
} else if trimmed.starts_with("cc_test(") {
("cc_test(", X86TargetKind::Executable)
} else {
continue;
};
if let Some(name_start) = trimmed.find("name") {
let after_name = &trimmed[name_start + 4..];
if let Some(eq) = after_name.find('=') {
let val = after_name[eq + 1..]
.trim()
.trim_matches('"')
.trim_matches('\'');
if let Some(comma) = val.find(',') {
targets.push(X86BuildTarget {
name: val[..comma]
.trim()
.trim_matches('"')
.trim_matches('\'')
.to_string(),
kind,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
} else {
targets.push(X86BuildTarget {
name: val.to_string(),
kind,
sources: Vec::new(),
defines: Vec::new(),
includes: Vec::new(),
compiler_flags: Vec::new(),
linker_flags: Vec::new(),
dependencies: Vec::new(),
});
}
}
}
}
break;
}
}
targets
}
pub fn find_special_files(root: &Path) -> HashMap<String, PathBuf> {
let mut special = HashMap::new();
let candidates = [
("configure", "configure"),
("config.h.in", "config_h_in"),
("config.h", "config_h"),
("CHANGELOG", "changelog"),
("CHANGELOG.md", "changelog_md"),
("README", "readme"),
("README.md", "readme_md"),
("LICENSE", "license"),
("COPYING", "copying"),
("VERSION", "version_file"),
("meson_options.txt", "meson_options"),
("CMakePresets.json", "cmake_presets"),
(".gitmodules", "gitmodules"),
("build.rs", "cargo_build_rs"),
];
for (filename, key) in &candidates {
let path = root.join(filename);
if path.exists() {
special.insert(key.to_string(), path);
}
}
special
}
pub fn detect_version(
root: &Path,
system: X86BuildSystem,
variables: &HashMap<String, String>,
) -> Option<String> {
for key in &[
"PROJECT_VERSION",
"PACKAGE_VERSION",
"VERSION",
"CARGO_PKG_VERSION",
] {
if let Some(v) = variables.get(*key) {
return Some(v.clone());
}
}
if let Ok(content) = fs::read_to_string(root.join("VERSION")) {
let v = content.trim();
if !v.is_empty() {
return Some(v.to_string());
}
}
None
}
pub fn detect_description(
root: &Path,
system: X86BuildSystem,
variables: &HashMap<String, String>,
) -> Option<String> {
for key in &["PROJECT_DESCRIPTION", "PACKAGE_DESCRIPTION", "DESCRIPTION"] {
if let Some(v) = variables.get(*key) {
return Some(v.clone());
}
}
for readme in &["README.md", "README", "README.rst"] {
if let Ok(content) = fs::read_to_string(root.join(readme)) {
if let Some(first_line) = content.lines().next() {
let desc = first_line.trim().trim_start_matches('#').trim();
if !desc.is_empty() {
return Some(desc.to_string());
}
}
}
}
None
}
pub fn discover_sub_projects(root: &Path) -> Vec<X86ProjectDetector> {
let mut subs = Vec::new();
if let Ok(entries) = fs::read_dir(root) {
for entry in entries.flatten() {
let path = entry.path();
if !path.is_dir() {
continue;
}
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with('.') || name == "build" || name == "target" {
continue;
}
let sub_system = X86ProjectDetector::detect_build_system(&path);
if sub_system != X86BuildSystem::Unknown {
let sub = Self::scan(&path);
subs.push(sub);
}
}
}
subs
}
pub fn collect_sources(&self) -> Vec<PathBuf> {
let mut sources = Vec::new();
for (dir, files) in &self.source_groups {
for file in files {
let full = self.project_root.join(dir).join(file);
sources.push(full);
}
}
if sources.is_empty() {
let exts: HashSet<&str> = ["c", "cc", "cpp", "cxx", "c++", "C"]
.iter()
.cloned()
.collect();
for file in &self.all_files {
if let Some(ext) = file.extension().and_then(|e| e.to_str()) {
if exts.contains(ext) {
sources.push(self.project_root.join(file));
}
}
}
}
sources
}
pub fn collect_include_dirs(&self) -> Vec<PathBuf> {
let mut dirs = Vec::new();
dirs.push(self.project_root.clone());
dirs.push(self.project_root.join("include"));
dirs.push(self.project_root.join("src"));
for dir in self.header_groups.keys() {
dirs.push(self.project_root.join(dir));
}
dirs.sort();
dirs.dedup();
dirs.retain(|d| d.exists());
dirs
}
pub fn project_name(&self) -> &str {
self.project_root
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("unknown")
}
fn extract_parens(line: &str, keyword: &str) -> Option<String> {
let after_kw = line.trim_start_matches(keyword);
let after_kw = after_kw.trim_start();
if !after_kw.starts_with('(') {
return None;
}
let inner = &after_kw[1..];
let mut depth = 0;
for (i, c) in inner.char_indices() {
match c {
'(' => depth += 1,
')' if depth == 0 => return Some(inner[..i].to_string()),
')' => depth -= 1,
_ => {}
}
}
None
}
}
pub struct X86ConfigureDetector;
#[derive(Debug, Clone)]
pub struct X86BuildConfiguration {
pub defines: Vec<(String, Option<String>)>,
pub include_dirs: Vec<PathBuf>,
pub lib_dirs: Vec<PathBuf>,
pub libraries: Vec<String>,
pub cflags: Vec<String>,
pub cxxflags: Vec<String>,
pub ldflags: Vec<String>,
pub arch_flags: Vec<String>,
pub platform: X86Platform,
pub architecture: X86ArchVariant,
pub features: X86Features,
pub language_standard: String,
pub use_exceptions: Option<bool>,
pub use_rtti: Option<bool>,
pub use_lto: Option<bool>,
pub simd_level: X86SIMDLevel,
}
impl X86BuildConfiguration {
pub fn all_compiler_flags(&self) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
flags.extend(self.cflags.clone());
flags.extend(self.cxxflags.clone());
flags.extend(self.arch_flags.clone());
for (name, value) in &self.defines {
match value {
Some(v) => flags.push(format!("-D{}={}", name, v)),
None => flags.push(format!("-D{}", name)),
}
}
for dir in &self.include_dirs {
flags.push(format!("-I{}", dir.display()));
}
flags
}
pub fn all_linker_flags(&self) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
flags.extend(self.ldflags.clone());
for dir in &self.lib_dirs {
flags.push(format!("-L{}", dir.display()));
}
for lib in &self.libraries {
flags.push(format!("-l{}", lib));
}
flags
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Platform {
Linux,
Windows,
Darwin,
FreeBSD,
Unix,
Unknown,
}
impl X86Platform {
pub fn as_str(&self) -> &'static str {
match self {
Self::Linux => "linux",
Self::Windows => "windows",
Self::Darwin => "darwin",
Self::FreeBSD => "freebsd",
Self::Unix => "unix",
Self::Unknown => "unknown",
}
}
pub fn host() -> Self {
if cfg!(target_os = "linux") {
Self::Linux
} else if cfg!(target_os = "windows") {
Self::Windows
} else if cfg!(target_os = "macos") {
Self::Darwin
} else if cfg!(target_os = "freebsd") {
Self::FreeBSD
} else if cfg!(unix) {
Self::Unix
} else {
Self::Unknown
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ArchVariant {
X86_32,
X86_64,
X86_X32,
}
impl X86ArchVariant {
pub fn as_str(&self) -> &'static str {
match self {
Self::X86_32 => "x86_32",
Self::X86_64 => "x86_64",
Self::X86_X32 => "x86_x32",
}
}
pub fn is_64_bit(&self) -> bool {
matches!(self, Self::X86_64 | Self::X86_X32)
}
pub fn pointer_width(&self) -> u32 {
match self {
Self::X86_32 => 32,
Self::X86_64 => 64,
Self::X86_X32 => 32,
}
}
pub fn stack_alignment(&self) -> u32 {
match self {
Self::X86_32 => X86_STACK_ALIGNMENT_32,
Self::X86_64 => X86_STACK_ALIGNMENT_64,
Self::X86_X32 => 16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86SIMDLevel {
None,
MMX,
SSE,
SSE2,
SSE3,
SSSE3,
SSE41,
SSE42,
AVX,
AVX2,
AVX512F,
}
impl X86SIMDLevel {
pub fn as_str(&self) -> &'static str {
match self {
Self::None => "none",
Self::MMX => "mmx",
Self::SSE => "sse",
Self::SSE2 => "sse2",
Self::SSE3 => "sse3",
Self::SSSE3 => "ssse3",
Self::SSE41 => "sse4.1",
Self::SSE42 => "sse4.2",
Self::AVX => "avx",
Self::AVX2 => "avx2",
Self::AVX512F => "avx512f",
}
}
pub fn compile_flags(&self) -> Vec<&'static str> {
match self {
Self::None => vec![],
Self::MMX => vec!["-mmmx"],
Self::SSE => vec!["-msse"],
Self::SSE2 => vec!["-msse2"],
Self::SSE3 => vec!["-msse3"],
Self::SSSE3 => vec!["-mssse3"],
Self::SSE41 => vec!["-msse4.1"],
Self::SSE42 => vec!["-msse4.2"],
Self::AVX => vec!["-mavx"],
Self::AVX2 => vec!["-mavx2"],
Self::AVX512F => vec!["-mavx512f"],
}
}
}
#[derive(Debug, Clone, Default)]
pub struct X86Features {
pub mmx: bool,
pub sse: bool,
pub sse2: bool,
pub sse3: bool,
pub ssse3: bool,
pub sse41: bool,
pub sse42: bool,
pub avx: bool,
pub avx2: bool,
pub avx512f: bool,
pub avx512cd: bool,
pub avx512bw: bool,
pub avx512dq: bool,
pub avx512vl: bool,
pub fma: bool,
pub bmi: bool,
pub bmi2: bool,
pub lzcnt: bool,
pub popcnt: bool,
pub rdrand: bool,
pub rdseed: bool,
pub aes: bool,
pub sha: bool,
pub adx: bool,
pub sgx: bool,
}
impl X86Features {
pub fn highest_simd(&self) -> X86SIMDLevel {
if self.avx512f {
X86SIMDLevel::AVX512F
} else if self.avx2 {
X86SIMDLevel::AVX2
} else if self.avx {
X86SIMDLevel::AVX
} else if self.sse42 {
X86SIMDLevel::SSE42
} else if self.sse41 {
X86SIMDLevel::SSE41
} else if self.ssse3 {
X86SIMDLevel::SSSE3
} else if self.sse3 {
X86SIMDLevel::SSE3
} else if self.sse2 {
X86SIMDLevel::SSE2
} else if self.sse {
X86SIMDLevel::SSE
} else if self.mmx {
X86SIMDLevel::MMX
} else {
X86SIMDLevel::None
}
}
}
impl X86ConfigureDetector {
pub fn detect(
root: &Path,
detector: &X86ProjectDetector,
target_triple: &str,
) -> X86BuildConfiguration {
let platform = Self::detect_platform(target_triple);
let architecture = Self::detect_architecture(target_triple);
let features = Self::detect_features(detector);
let defines = Self::detect_defines(root, detector, platform, architecture, &features);
let include_dirs = Self::detect_include_dirs(root, detector);
let lib_dirs = Self::detect_lib_dirs(root, detector, platform);
let libraries = Self::detect_libraries(root, detector);
let cflags = Self::detect_cflags(root, detector, platform, architecture, &features);
let cxxflags = Self::detect_cxxflags(root, detector);
let ldflags = Self::detect_ldflags(root, detector, platform, architecture);
let arch_flags = Self::detect_arch_flags(architecture, &features);
let language_standard = Self::detect_language_standard(root, detector);
let use_exceptions = Self::detect_exceptions(root);
let use_rtti = Self::detect_rtti(root);
let use_lto = Self::detect_lto(root);
let simd_level = features.highest_simd();
X86BuildConfiguration {
defines,
include_dirs,
lib_dirs,
libraries,
cflags,
cxxflags,
ldflags,
arch_flags,
platform,
architecture,
features,
language_standard,
use_exceptions,
use_rtti,
use_lto,
simd_level,
}
}
pub fn detect_platform(triple: &str) -> X86Platform {
let lower = triple.to_lowercase();
if lower.contains("linux") {
X86Platform::Linux
} else if lower.contains("windows") || lower.contains("msvc") || lower.contains("mingw") {
X86Platform::Windows
} else if lower.contains("darwin") || lower.contains("macos") || lower.contains("apple") {
X86Platform::Darwin
} else if lower.contains("freebsd") {
X86Platform::FreeBSD
} else if lower.contains("unix") || lower.contains("bsd") || lower.contains("solaris") {
X86Platform::Unix
} else {
X86Platform::Unknown
}
}
pub fn detect_architecture(triple: &str) -> X86ArchVariant {
let lower = triple.to_lowercase();
if lower.contains("x32") || lower.contains("x86_64.*x32") {
X86ArchVariant::X86_X32
} else if lower.contains("x86_64") || lower.contains("amd64") {
X86ArchVariant::X86_64
} else if lower.contains("i386")
|| lower.contains("i486")
|| lower.contains("i586")
|| lower.contains("i686")
|| lower.contains("x86")
{
X86ArchVariant::X86_32
} else {
X86ArchVariant::X86_64 }
}
pub fn detect_features(detector: &X86ProjectDetector) -> X86Features {
let mut features = X86Features::default();
let arch = Self::detect_architecture("x86_64-unknown-linux-gnu");
for (key, value) in &detector.variables {
let key_upper = key.to_uppercase();
let val_lower = value.to_lowercase();
if key_upper.contains("SSE") || val_lower.contains("msse") {
if val_lower.contains("sse2") || key_upper.contains("SSE2") {
features.sse2 = true;
features.sse = true;
features.mmx = true;
}
if val_lower.contains("sse3") || key_upper.contains("SSE3") {
features.sse3 = true;
features.sse2 = true;
features.sse = true;
features.mmx = true;
}
if val_lower.contains("ssse3") || key_upper.contains("SSSE3") {
features.ssse3 = true;
}
if val_lower.contains("sse4.1") || key_upper.contains("SSE4_1") {
features.sse41 = true;
}
if val_lower.contains("sse4.2") || key_upper.contains("SSE4_2") {
features.sse42 = true;
}
}
if key_upper.contains("AVX") || val_lower.contains("mavx") {
if val_lower.contains("avx512") || key_upper.contains("AVX512") {
features.avx512f = true;
}
if val_lower.contains("avx2") || key_upper.contains("AVX2") {
features.avx2 = true;
}
features.avx = true;
features.sse42 = true;
features.sse41 = true;
}
if key_upper.contains("FMA") || val_lower.contains("mfma") {
features.fma = true;
}
if key_upper.contains("BMI") || val_lower.contains("mbmi") {
if key_upper.contains("BMI2") || val_lower.contains("bmi2") {
features.bmi2 = true;
}
features.bmi = true;
}
}
features
}
pub fn detect_defines(
root: &Path,
detector: &X86ProjectDetector,
platform: X86Platform,
arch: X86ArchVariant,
features: &X86Features,
) -> Vec<(String, Option<String>)> {
let mut defines: Vec<(String, Option<String>)> = Vec::new();
match platform {
X86Platform::Linux => {
defines.push(("__linux__".into(), Some("1".into())));
defines.push(("__unix__".into(), Some("1".into())));
}
X86Platform::Windows => {
defines.push(("_WIN32".into(), Some("1".into())));
if arch.is_64_bit() {
defines.push(("_WIN64".into(), Some("1".into())));
}
}
X86Platform::Darwin => {
defines.push(("__APPLE__".into(), Some("1".into())));
defines.push(("__MACH__".into(), Some("1".into())));
}
X86Platform::FreeBSD => {
defines.push(("__FreeBSD__".into(), Some("1".into())));
defines.push(("__unix__".into(), Some("1".into())));
}
_ => {}
}
if arch.is_64_bit() {
defines.push(("__x86_64__".into(), Some("1".into())));
defines.push(("__amd64__".into(), Some("1".into())));
defines.push(("__LP64__".into(), Some("1".into())));
} else {
defines.push(("__i386__".into(), Some("1".into())));
}
if features.sse {
defines.push(("__SSE__".into(), Some("1".into())));
}
if features.sse2 {
defines.push(("__SSE2__".into(), Some("1".into())));
}
if features.sse3 {
defines.push(("__SSE3__".into(), Some("1".into())));
}
if features.ssse3 {
defines.push(("__SSSE3__".into(), Some("1".into())));
}
if features.sse41 {
defines.push(("__SSE4_1__".into(), Some("1".into())));
}
if features.sse42 {
defines.push(("__SSE4_2__".into(), Some("1".into())));
}
if features.avx {
defines.push(("__AVX__".into(), Some("1".into())));
}
if features.avx2 {
defines.push(("__AVX2__".into(), Some("1".into())));
}
if features.avx512f {
defines.push(("__AVX512F__".into(), Some("1".into())));
}
if features.fma {
defines.push(("__FMA__".into(), Some("1".into())));
}
if features.bmi {
defines.push(("__BMI__".into(), Some("1".into())));
}
if features.bmi2 {
defines.push(("__BMI2__".into(), Some("1".into())));
}
for (key, value) in &detector.variables {
if key.starts_with("HAVE_") || key.starts_with("ENABLE_") || key.starts_with("WITH_") {
if value == "1" || value.to_lowercase() == "on" || value.to_lowercase() == "yes" {
defines.push((key.clone(), Some("1".into())));
}
}
}
if let Ok(content) = fs::read_to_string(root.join("config.h")) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("#define") {
let parts: Vec<&str> = trimmed.split_whitespace().collect();
if parts.len() >= 2 && parts[1].starts_with("HAVE_") {
let value = if parts.len() >= 3 {
Some(parts[2].to_string())
} else {
Some("1".to_string())
};
defines.push((parts[1].to_string(), value));
}
}
}
}
defines.sort();
defines.dedup();
defines
}
pub fn detect_include_dirs(root: &Path, detector: &X86ProjectDetector) -> Vec<PathBuf> {
let mut dirs = detector.collect_include_dirs();
for (key, value) in &detector.variables {
let key_upper = key.to_uppercase();
if key_upper.contains("INCLUDE") || key_upper.contains("INC") {
for part in value.split_whitespace() {
if part.starts_with("-I") {
let path = &part[2..];
let full = if Path::new(path).is_absolute() {
PathBuf::from(path)
} else {
root.join(path)
};
if full.exists() && !dirs.contains(&full) {
dirs.push(full);
}
}
}
}
}
dirs.sort();
dirs.dedup();
dirs
}
pub fn detect_lib_dirs(
root: &Path,
detector: &X86ProjectDetector,
platform: X86Platform,
) -> Vec<PathBuf> {
let mut dirs = Vec::new();
dirs.push(root.join("lib"));
dirs.push(root.join("build").join("lib"));
match platform {
X86Platform::Linux => {
dirs.push(PathBuf::from("/usr/lib"));
dirs.push(PathBuf::from("/usr/lib/x86_64-linux-gnu"));
dirs.push(PathBuf::from("/usr/local/lib"));
}
X86Platform::Windows => {
dirs.push(PathBuf::from("C:\\Windows\\System32"));
}
X86Platform::Darwin => {
dirs.push(PathBuf::from("/usr/lib"));
dirs.push(PathBuf::from("/usr/local/lib"));
}
_ => {}
}
for (key, value) in &detector.variables {
let key_upper = key.to_uppercase();
if key_upper.contains("LIBDIR")
|| key_upper.contains("LIB_DIR")
|| key_upper == "LDFLAGS"
{
for part in value.split_whitespace() {
if part.starts_with("-L") {
let path = &part[2..];
let full = if Path::new(path).is_absolute() {
PathBuf::from(path)
} else {
root.join(path)
};
if !dirs.contains(&full) {
dirs.push(full);
}
}
}
}
}
dirs.sort();
dirs.dedup();
dirs
}
pub fn detect_libraries(root: &Path, detector: &X86ProjectDetector) -> Vec<String> {
let mut libs = Vec::new();
for (key, value) in &detector.variables {
let key_upper = key.to_uppercase();
if key_upper.contains("LIBS") || key_upper == "LDLIBS" || key_upper == "LDFLAGS" {
for part in value.split_whitespace() {
if part.starts_with("-l") {
let lib = part[2..].to_string();
if !lib.is_empty() && !libs.contains(&lib) {
libs.push(lib);
}
}
}
}
}
libs
}
pub fn detect_cflags(
root: &Path,
detector: &X86ProjectDetector,
platform: X86Platform,
arch: X86ArchVariant,
features: &X86Features,
) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
for key in &["CFLAGS", "cflags", "CMAKE_C_FLAGS"] {
if let Some(val) = detector.variables.get(*key) {
flags.extend(val.split_whitespace().map(String::from));
}
}
if !flags.iter().any(|f| f.starts_with("-std=")) {
flags.push("-std=c11".into());
}
if !flags.iter().any(|f| f.starts_with("-O")) {
flags.push("-O2".into());
}
if arch.is_64_bit() {
if !flags.iter().any(|f| f == "-m64" || f == "-m32") {
flags.push("-m64".into());
}
} else {
if !flags.iter().any(|f| f == "-m32" || f == "-m64") {
flags.push("-m32".into());
}
}
if platform == X86Platform::Linux || platform == X86Platform::Unix {
if !flags.iter().any(|f| f == "-fPIC" || f == "-fpic") {
flags.push("-fPIC".into());
}
}
for feat_flag in features.highest_simd().compile_flags() {
if !flags.contains(&feat_flag.to_string()) {
flags.push(feat_flag.to_string());
}
}
flags
}
pub fn detect_cxxflags(root: &Path, detector: &X86ProjectDetector) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
for key in &["CXXFLAGS", "cxxflags", "CMAKE_CXX_FLAGS"] {
if let Some(val) = detector.variables.get(*key) {
flags.extend(val.split_whitespace().map(String::from));
}
}
if !flags.iter().any(|f| f.starts_with("-std=")) {
flags.push("-std=c++17".into());
}
flags
}
pub fn detect_ldflags(
root: &Path,
detector: &X86ProjectDetector,
platform: X86Platform,
arch: X86ArchVariant,
) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
for key in &["LDFLAGS", "ldflags", "CMAKE_EXE_LINKER_FLAGS"] {
if let Some(val) = detector.variables.get(*key) {
flags.extend(val.split_whitespace().map(String::from));
}
}
if platform == X86Platform::Linux || platform == X86Platform::Unix {
if !flags.contains(&"-Wl,--no-undefined".to_string()) {
}
}
flags
}
pub fn detect_arch_flags(arch: X86ArchVariant, features: &X86Features) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
if arch.is_64_bit() {
flags.push("-m64".into());
} else {
flags.push("-m32".into());
}
flags.extend(
features
.highest_simd()
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
pub fn detect_language_standard(root: &Path, detector: &X86ProjectDetector) -> String {
if detector.build_system == X86BuildSystem::CMake {
if let Ok(content) = fs::read_to_string(root.join("CMakeLists.txt")) {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.contains("C_STANDARD") {
if let Some(val) = trimmed.split_whitespace().last() {
let val = val.trim_matches(')').trim_matches('"');
return format!("c{}", val);
}
}
if trimmed.contains("CXX_STANDARD") {
if let Some(val) = trimmed.split_whitespace().last() {
let val = val.trim_matches(')').trim_matches('"');
return format!("c++{}", val);
}
}
}
}
}
"c11".to_string()
}
pub fn detect_exceptions(root: &Path) -> Option<bool> {
if root.join("CMakeLists.txt").exists() {
if let Ok(content) = fs::read_to_string(root.join("CMakeLists.txt")) {
let has_exc =
content.contains("CXX_EXCEPTIONS") || content.contains("enable_exceptions");
let no_exc =
content.contains("no-exceptions") || content.contains("DISABLE_EXCEPTIONS");
if has_exc && !no_exc {
return Some(true);
}
if no_exc {
return Some(false);
}
}
}
None
}
pub fn detect_rtti(root: &Path) -> Option<bool> {
if root.join("CMakeLists.txt").exists() {
if let Ok(content) = fs::read_to_string(root.join("CMakeLists.txt")) {
if content.contains("no-rtti") || content.contains("DISABLE_RTTI") {
return Some(false);
}
}
}
None
}
pub fn detect_lto(root: &Path) -> Option<bool> {
if root.join("CMakeLists.txt").exists() {
if let Ok(content) = fs::read_to_string(root.join("CMakeLists.txt")) {
if content.contains("INTERPROCEDURAL_OPTIMIZATION")
|| content.contains("ENABLE_LTO")
|| content.contains("-flto")
{
return Some(true);
}
}
}
None
}
}
pub struct X86DependencyResolver;
#[derive(Debug, Clone, Default)]
pub struct X86DependencyMap {
pub dependencies: HashMap<String, X86Dependency>,
pub header_libs: Vec<X86HeaderLibrary>,
pub system_packages: Vec<X86SystemPackage>,
pub pkgconfig_modules: Vec<X86PkgConfigModule>,
pub git_submodules: Vec<X86GitSubmodule>,
}
#[derive(Debug, Clone)]
pub struct X86Dependency {
pub name: String,
pub version: Option<String>,
pub include_dirs: Vec<PathBuf>,
pub lib_dirs: Vec<PathBuf>,
pub libraries: Vec<String>,
pub cflags: Vec<String>,
pub found: bool,
pub source: X86DepDiscoverySource,
}
#[derive(Debug, Clone)]
pub enum X86DepDiscoverySource {
PkgConfig(String),
CMakeFindPackage,
SystemPackage,
HeaderOnly,
GitSubmodule,
Manual,
NotFound,
}
#[derive(Debug, Clone)]
pub struct X86HeaderLibrary {
pub name: String,
pub header_path: PathBuf,
pub version: Option<String>,
pub extra_includes: Vec<PathBuf>,
}
#[derive(Debug, Clone)]
pub struct X86SystemPackage {
pub name: String,
pub manager: X86PackageManager,
pub installed: bool,
pub version: Option<String>,
pub dev_package: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PackageManager {
Apt,
Yum,
Brew,
Pacman,
Zypper,
Portage,
Nix,
Pkg,
Unknown,
}
impl X86PackageManager {
pub fn as_str(&self) -> &'static str {
match self {
Self::Apt => "apt",
Self::Yum => "yum",
Self::Brew => "brew",
Self::Pacman => "pacman",
Self::Zypper => "zypper",
Self::Portage => "emerge",
Self::Nix => "nix",
Self::Pkg => "pkg",
Self::Unknown => "unknown",
}
}
pub fn detect() -> Self {
if Command::new("apt").arg("--version").output().is_ok() {
return Self::Apt;
}
if Command::new("dnf").arg("--version").output().is_ok() {
return Self::Yum;
}
if Command::new("yum").arg("--version").output().is_ok() {
return Self::Yum;
}
if Command::new("brew").arg("--version").output().is_ok() {
return Self::Brew;
}
if Command::new("pacman").arg("--version").output().is_ok() {
return Self::Pacman;
}
if Command::new("zypper").arg("--version").output().is_ok() {
return Self::Zypper;
}
if Command::new("emerge").arg("--version").output().is_ok() {
return Self::Portage;
}
if Command::new("nix").arg("--version").output().is_ok() {
return Self::Nix;
}
if Command::new("pkg").arg("--version").output().is_ok() {
return Self::Pkg;
}
Self::Unknown
}
pub fn install_command(&self, package: &str) -> Option<Vec<String>> {
match self {
Self::Apt => Some(vec![
"sudo".into(),
"apt-get".into(),
"install".into(),
"-y".into(),
package.into(),
]),
Self::Yum => Some(vec![
"sudo".into(),
"dnf".into(),
"install".into(),
"-y".into(),
package.into(),
]),
Self::Brew => Some(vec!["brew".into(), "install".into(), package.into()]),
Self::Pacman => Some(vec![
"sudo".into(),
"pacman".into(),
"-S".into(),
"--noconfirm".into(),
package.into(),
]),
Self::Zypper => Some(vec![
"sudo".into(),
"zypper".into(),
"install".into(),
"-y".into(),
package.into(),
]),
Self::Portage => Some(vec!["sudo".into(), "emerge".into(), package.into()]),
Self::Nix => Some(vec![
"nix-env".into(),
"-iA".into(),
format!("nixpkgs.{}", package),
]),
Self::Pkg => Some(vec![
"sudo".into(),
"pkg".into(),
"install".into(),
"-y".into(),
package.into(),
]),
Self::Unknown => None,
}
}
}
#[derive(Debug, Clone)]
pub struct X86PkgConfigModule {
pub name: String,
pub version: Option<String>,
pub found: bool,
pub pc_info: Option<X86PcFileInfo>,
}
#[derive(Debug, Clone)]
pub struct X86GitSubmodule {
pub path: String,
pub url: String,
pub initialized: bool,
pub has_build_system: bool,
}
impl X86DependencyResolver {
pub fn resolve(
root: &Path,
detector: &X86ProjectDetector,
config: &X86BuildConfiguration,
) -> X86DependencyMap {
let mut map = X86DependencyMap::default();
map.pkgconfig_modules = Self::detect_pkgconfig_deps(detector);
map.system_packages = Self::detect_system_packages(detector);
map.dependencies = Self::detect_cmake_deps(root, detector);
map.header_libs = Self::detect_header_libs(root);
map.git_submodules = Self::detect_git_submodules(root);
for pkg in &map.pkgconfig_modules {
if let Some(ref info) = pkg.pc_info {
map.dependencies.insert(
pkg.name.clone(),
X86Dependency {
name: pkg.name.clone(),
version: Some(info.version.clone()),
include_dirs: info.include_dirs.clone(),
lib_dirs: info.lib_dirs.clone(),
libraries: info.libs.clone(),
cflags: info.cflags.clone(),
found: true,
source: X86DepDiscoverySource::PkgConfig(pkg.name.clone()),
},
);
}
}
map
}
fn detect_pkgconfig_deps(detector: &X86ProjectDetector) -> Vec<X86PkgConfigModule> {
let mut modules = Vec::new();
for (key, value) in &detector.variables {
let key_upper = key.to_uppercase();
if key_upper.contains("PKG_CHECK_MODULES") || value.contains("pkg-config") {
let inner = X86ProjectDetector::extract_parens(value, "PKG_CHECK_MODULES");
let module_list = inner.unwrap_or(value.clone());
for part in module_list.split_whitespace().skip(1) {
let name = part.split_whitespace().next().unwrap_or(part);
let name = name.trim().trim_matches('"').trim_matches('\'');
if !name.is_empty()
&& name != "REQUIRED"
&& !name.starts_with('>')
&& !name.starts_with('<')
&& !name.starts_with('=')
{
let mut pc_info = None;
if let Ok(output) = Command::new("pkg-config")
.args(&["--cflags", "--libs", name])
.output()
{
if output.status.success() {
pc_info =
X86PackageConfig::parse_pc_file_for(name).ok().or_else(|| {
Some(X86PcFileInfo {
name: name.to_string(),
version: "unknown".to_string(),
description: String::new(),
prefix: PathBuf::from("/usr"),
include_dirs: Vec::new(),
lib_dirs: Vec::new(),
libs: Vec::new(),
cflags: Vec::new(),
requires: Vec::new(),
requires_private: Vec::new(),
})
});
}
}
modules.push(X86PkgConfigModule {
name: name.to_string(),
version: None,
found: pc_info.is_some(),
pc_info,
});
}
}
}
}
modules
}
fn detect_system_packages(detector: &X86ProjectDetector) -> Vec<X86SystemPackage> {
let mut packages = Vec::new();
let manager = X86PackageManager::detect();
let common_libs = [
("zlib", "zlib1g-dev"),
("openssl", "libssl-dev"),
("curl", "libcurl4-openssl-dev"),
("sqlite3", "libsqlite3-dev"),
("libpng", "libpng-dev"),
("libjpeg", "libjpeg-dev"),
("libxml2", "libxml2-dev"),
("pcre", "libpcre3-dev"),
("readline", "libreadline-dev"),
("ncurses", "libncurses5-dev"),
];
for (key, value) in &detector.variables {
let key_upper = key.to_uppercase();
if key_upper.contains("REQUIRES") || key_upper.contains("DEPENDS") {
for dep in value.split_whitespace() {
let dep = dep
.trim()
.trim_matches(',')
.trim_matches('"')
.trim_matches('\'');
if !dep.is_empty() {
let dev = common_libs
.iter()
.find(|(name, _)| *name == dep)
.map(|(_, dev)| dev.to_string());
packages.push(X86SystemPackage {
name: dep.to_string(),
manager,
installed: false, version: None,
dev_package: dev,
});
}
}
}
}
packages
}
fn detect_cmake_deps(
root: &Path,
detector: &X86ProjectDetector,
) -> HashMap<String, X86Dependency> {
let mut deps = HashMap::new();
if detector.build_system != X86BuildSystem::CMake {
return deps;
}
let path = root.join("CMakeLists.txt");
if let Ok(content) = fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim().to_lowercase();
if trimmed.starts_with("find_package(") {
let inner = X86ProjectDetector::extract_parens(line.trim(), "find_package");
if let Some(inner) = inner {
let parts: Vec<&str> = inner.split_whitespace().collect();
if !parts.is_empty() {
let name = parts[0].to_string();
let version = if parts.len() >= 2 {
let v = parts[1];
if v.chars().next().map_or(false, |c| c.is_ascii_digit()) {
Some(v.to_string())
} else {
None
}
} else {
None
};
let required = inner.to_uppercase().contains("REQUIRED");
deps.insert(
name.clone(),
X86Dependency {
name,
version,
include_dirs: Vec::new(),
lib_dirs: Vec::new(),
libraries: Vec::new(),
cflags: Vec::new(),
found: !required, source: X86DepDiscoverySource::CMakeFindPackage,
},
);
}
}
}
}
}
deps
}
fn detect_header_libs(root: &Path) -> Vec<X86HeaderLibrary> {
let mut libs = Vec::new();
let known_header_libs = [
("uthash", &["uthash.h", "utlist.h", "utstring.h"][..]),
(
"stb",
&["stb_image.h", "stb_image_write.h", "stb_truetype.h"],
),
("doctest", &["doctest.h"]),
("cxxopts", &["cxxopts.hpp"]),
("json", &["json.hpp", "json_fwd.hpp"]),
("catch2", &["catch.hpp", "catch_amalgamated.hpp"]),
("expected", &["expected.hpp", "tl/expected.hpp"]),
("function2", &["function2.hpp", "function2/function2.hpp"]),
];
for (name, headers) in &known_header_libs {
for header in *headers {
let full = root.join(header);
if full.exists() {
libs.push(X86HeaderLibrary {
name: name.to_string(),
header_path: full,
version: None,
extra_includes: Vec::new(),
});
break;
}
for subdir in &["include", "src", "single_include"] {
let alt = root.join(subdir).join(header);
if alt.exists() {
libs.push(X86HeaderLibrary {
name: name.to_string(),
header_path: alt,
version: None,
extra_includes: vec![root.join(subdir)],
});
break;
}
}
}
}
libs
}
fn detect_git_submodules(root: &Path) -> Vec<X86GitSubmodule> {
let mut subs = Vec::new();
let gitmodules = root.join(".gitmodules");
if let Ok(content) = fs::read_to_string(&gitmodules) {
let mut current_path = String::new();
let mut current_url = String::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("[submodule") {
if !current_path.is_empty() {
subs.push(X86GitSubmodule {
path: std::mem::take(&mut current_path),
url: std::mem::take(&mut current_url),
initialized: root.join(¤t_path).exists(),
has_build_system: X86ProjectDetector::detect_build_system(
&root.join(¤t_path),
) != X86BuildSystem::Unknown,
});
}
} else if trimmed.starts_with("path") {
if let Some(val) = trimmed.split('=').nth(1) {
current_path = val.trim().to_string();
}
} else if trimmed.starts_with("url") {
if let Some(val) = trimmed.split('=').nth(1) {
current_url = val.trim().to_string();
}
}
}
if !current_path.is_empty() {
subs.push(X86GitSubmodule {
path: current_path.clone(),
url: current_url,
initialized: root.join(¤t_path).exists(),
has_build_system: X86ProjectDetector::detect_build_system(
&root.join(¤t_path),
) != X86BuildSystem::Unknown,
});
}
}
subs
}
pub fn generate_report(map: &X86DependencyMap) -> String {
let mut report = String::new();
report.push_str("── Dependency Resolution Report ──\n");
report.push_str(&format!(
" Dependencies resolved: {}\n",
map.dependencies.len()
));
for (name, dep) in &map.dependencies {
let status = if dep.found { "✓" } else { "✗" };
report.push_str(&format!(" {} {} - {:?}\n", status, name, dep.source));
}
report.push_str(&format!(
" Header-only libs found: {}\n",
map.header_libs.len()
));
for lib in &map.header_libs {
report.push_str(&format!(
" {} -> {}\n",
lib.name,
lib.header_path.display()
));
}
report.push_str(&format!(
" System packages: {}\n",
map.system_packages.len()
));
for pkg in &map.system_packages {
report.push_str(&format!(" {} ({})\n", pkg.name, pkg.manager.as_str()));
}
report.push_str(&format!(
" pkg-config modules: {}\n",
map.pkgconfig_modules.len()
));
for pkg in &map.pkgconfig_modules {
let status = if pkg.found { "found" } else { "missing" };
report.push_str(&format!(" {} -- {}\n", pkg.name, status));
}
report.push_str(&format!(" Git submodules: {}\n", map.git_submodules.len()));
for sub in &map.git_submodules {
let status = if sub.initialized {
"initialized"
} else {
"pending"
};
report.push_str(&format!(" {} => {} ({})\n", sub.path, sub.url, status));
}
report
}
}
pub trait X86ProjectConfigurable {
fn name(&self) -> &str;
fn sources(&self, root: &Path) -> Vec<PathBuf>;
fn defines(&self) -> Vec<(String, Option<String>)>;
fn includes(&self, root: &Path) -> Vec<PathBuf>;
fn compiler_flags(&self) -> Vec<String>;
fn linker_flags(&self) -> Vec<String>;
fn libraries(&self) -> Vec<String>;
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase;
fn test_commands(&self, build_dir: &Path) -> Vec<String>;
fn build_system(&self) -> X86BuildSystem;
fn notes(&self) -> Vec<String> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct X86CompilationDatabase {
pub project_name: String,
pub entries: Vec<X86CompileEntry>,
pub build_system: X86BuildSystem,
}
#[derive(Debug, Clone)]
pub struct X86CompileEntry {
pub directory: PathBuf,
pub source_file: PathBuf,
pub output_file: PathBuf,
pub command: String,
pub flags: Vec<String>,
pub defines: Vec<(String, Option<String>)>,
}
pub struct X86SQLiteConfig {
pub amalgamation: bool,
pub threading_mode: u8,
pub enable_fts5: bool,
pub enable_rtree: bool,
pub enable_json1: bool,
pub enable_geopoly: bool,
pub enable_math_functions: bool,
pub enable_session: bool,
pub enable_dbstat: bool,
pub omit_deprecated: bool,
pub simd_level: X86SIMDLevel,
}
impl Default for X86SQLiteConfig {
fn default() -> Self {
Self {
amalgamation: true,
threading_mode: 2,
enable_fts5: true,
enable_rtree: true,
enable_json1: true,
enable_geopoly: false,
enable_math_functions: false,
enable_session: false,
enable_dbstat: true,
omit_deprecated: true,
simd_level: X86SIMDLevel::SSE2,
}
}
}
impl X86ProjectConfigurable for X86SQLiteConfig {
fn name(&self) -> &str {
"sqlite"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
if self.amalgamation {
let sqlite3_c = root.join("sqlite3.c");
if sqlite3_c.exists() {
files.push(sqlite3_c);
if root.join("shell.c").exists() {
files.push(root.join("shell.c"));
}
return files;
}
}
for name in &[
"main.c",
"sqlite3.c",
"shell.c",
"icu.c",
"test1.c",
"test2.c",
] {
let path = root.join(name);
if path.exists() {
files.push(path);
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
(
"SQLITE_THREADSAFE".into(),
Some(self.threading_mode.to_string()),
),
("SQLITE_DEFAULT_MEMSTATUS".into(), Some("0".into())),
("SQLITE_DEFAULT_FOREIGN_KEYS".into(), Some("1".into())),
("SQLITE_DEFAULT_WAL_SYNCHRONOUS".into(), Some("1".into())),
("SQLITE_DEFAULT_CACHE_SIZE".into(), Some("-2000".into())),
("SQLITE_DEFAULT_MMAP_SIZE".into(), Some("268435456".into())),
("SQLITE_MAX_ATTACHED".into(), Some("10".into())),
("SQLITE_MAX_VARIABLE_NUMBER".into(), Some("250000".into())),
("SQLITE_ENABLE_COLUMN_METADATA".into(), None),
];
if self.omit_deprecated {
defs.push(("SQLITE_OMIT_DEPRECATED".into(), None));
defs.push(("SQLITE_OMIT_PROGRESS_CALLBACK".into(), None));
}
if self.enable_fts5 {
defs.push(("SQLITE_ENABLE_FTS5".into(), None));
defs.push(("SQLITE_ENABLE_FTS3_PARENTHESIS".into(), None));
}
if self.enable_rtree {
defs.push(("SQLITE_ENABLE_RTREE".into(), None));
}
if self.enable_json1 {
defs.push(("SQLITE_ENABLE_JSON1".into(), None));
}
if self.enable_geopoly {
defs.push(("SQLITE_ENABLE_GEOPOLY".into(), None));
}
if self.enable_math_functions {
defs.push(("SQLITE_ENABLE_MATH_FUNCTIONS".into(), None));
}
if self.enable_session {
defs.push(("SQLITE_ENABLE_SESSION".into(), None));
defs.push(("SQLITE_ENABLE_PREUPDATE_HOOK".into(), None));
}
if self.enable_dbstat {
defs.push(("SQLITE_ENABLE_DBSTAT_VTAB".into(), None));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.to_path_buf()]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
];
flags.extend(
self.simd_level
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec!["-lm".into(), "-ldl".into(), "-lpthread".into()];
if self.threading_mode > 0 {
flags.push("-lpthread".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
vec!["m".into(), "dl".into(), "pthread".into()]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
let defs = self.defines();
let flags = self.compiler_flags();
let defines_str = defs
.iter()
.map(|(k, v)| match v {
Some(val) => format!("-D{}={}", k, val),
None => format!("-D{}", k),
})
.collect::<Vec<_>>()
.join(" ");
let flags_str = flags.join(" ");
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
let cmd = format!(
"cc {} {} -c {} -o {}",
flags_str,
defines_str,
src.display(),
obj.display(),
);
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: cmd,
flags: flags.clone(),
defines: defs.clone(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{} --version", build_dir.join("sqlite3").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Make
}
fn notes(&self) -> Vec<String> {
vec![
"SQLite amalgamation build: all sources in single sqlite3.c".into(),
format!(
"Threading mode: {}",
match self.threading_mode {
0 => "single-thread",
1 => "serialized",
_ => "multi-thread",
}
),
]
}
}
pub struct X86ZlibConfig {
pub asm_optimizations: bool,
pub gz_trace: bool,
}
impl Default for X86ZlibConfig {
fn default() -> Self {
Self {
asm_optimizations: true,
gz_trace: false,
}
}
}
impl X86ProjectConfigurable for X86ZlibConfig {
fn name(&self) -> &str {
"zlib"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let sources = [
"adler32.c",
"compress.c",
"crc32.c",
"deflate.c",
"gzclose.c",
"gzlib.c",
"gzread.c",
"gzwrite.c",
"inflate.c",
"infback.c",
"inftrees.c",
"inffast.c",
"trees.c",
"uncompr.c",
"zutil.c",
];
sources
.iter()
.map(|s| root.join(s))
.filter(|p| p.exists())
.collect()
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![("ZLIB_CONST".into(), None)];
if !self.gz_trace {
defs.push(("NO_GZTRACE".into(), None));
}
if self.asm_optimizations {
defs.push(("ASMV".into(), None));
defs.push(("ASMINF".into(), None));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.to_path_buf(),
root.parent().unwrap_or(root).to_path_buf(),
]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wno-unused-parameter".into(),
"-D_LARGEFILE64_SOURCE=1".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
vec![]
}
fn libraries(&self) -> Vec<String> {
vec![]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
let flags = self.compiler_flags().join(" ");
let defs = self
.defines()
.iter()
.map(|(k, v)| match v {
Some(val) => format!("-D{}={}", k, val),
None => format!("-D{}", k),
})
.collect::<Vec<_>>()
.join(" ");
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} {} -c {} -o {}",
flags,
defs,
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{} example", build_dir.join("example").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Make
}
}
pub struct X86LibpngConfig {
pub has_zlib: bool,
pub zlib_include: Option<PathBuf>,
pub zlib_lib: Option<PathBuf>,
pub simd_level: X86SIMDLevel,
pub enable_arm_neon: bool,
pub enable_mips_msa: bool,
pub enable_powerpc_vsx: bool,
pub enable_hardware_optimizations: bool,
}
impl Default for X86LibpngConfig {
fn default() -> Self {
Self {
has_zlib: true,
zlib_include: None,
zlib_lib: None,
simd_level: X86SIMDLevel::SSE2,
enable_arm_neon: false,
enable_mips_msa: false,
enable_powerpc_vsx: false,
enable_hardware_optimizations: true,
}
}
}
impl X86ProjectConfigurable for X86LibpngConfig {
fn name(&self) -> &str {
"libpng"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let sources = [
"png.c",
"pngerror.c",
"pngget.c",
"pngmem.c",
"pngpread.c",
"pngread.c",
"pngrio.c",
"pngrtran.c",
"pngrutil.c",
"pngset.c",
"pngtrans.c",
"pngwio.c",
"pngwrite.c",
"pngwtran.c",
"pngwutil.c",
];
sources
.iter()
.map(|s| root.join(s))
.filter(|p| p.exists())
.collect()
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![("PNG_IMPEXP".into(), None)];
if self.has_zlib {
defs.push(("PNG_ZLIB_VER".into(), Some(r#""1.3""#.into())));
}
if self.enable_hardware_optimizations {
defs.push((
"PNG_ARM_NEON".into(),
Some(if self.enable_arm_neon { "1" } else { "0" }.into()),
));
defs.push((
"PNG_MIPS_MSA".into(),
Some(if self.enable_mips_msa { "1" } else { "0" }.into()),
));
defs.push((
"PNG_POWERPC_VSX".into(),
Some(if self.enable_powerpc_vsx { "1" } else { "0" }.into()),
));
defs.push((
"PNG_INTEL_SSE".into(),
Some(
if self.simd_level >= X86SIMDLevel::SSE2 {
"1"
} else {
"0"
}
.into(),
),
));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![root.to_path_buf()];
if let Some(ref zlib_inc) = self.zlib_include {
dirs.push(zlib_inc.clone());
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
];
flags.extend(
self.simd_level
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.has_zlib {
flags.push("-lz".into());
}
if let Some(ref zlib_lib) = self.zlib_lib {
flags.push(format!("-L{}", zlib_lib.display()));
}
flags
}
fn libraries(&self) -> Vec<String> {
if self.has_zlib {
vec!["z".into()]
} else {
vec![]
}
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
let flags = self.compiler_flags().join(" ");
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!("cc {} -c {} -o {}", flags, src.display(), obj.display()),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!(
"{} pngtest.png",
build_dir.join("pngtest").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![format!(
"Dependency chain: zlib required. SIMD level: {:?}",
self.simd_level
)]
}
}
pub struct X86JpegTurboConfig {
pub simd_level: X86SIMDLevel,
pub with_turbojpeg: bool,
pub with_java: bool,
pub with_12bit: bool,
pub with_arithmetic_enc: bool,
pub max_mem_mgr: Option<u32>,
}
impl Default for X86JpegTurboConfig {
fn default() -> Self {
Self {
simd_level: X86SIMDLevel::SSE2,
with_turbojpeg: true,
with_java: false,
with_12bit: false,
with_arithmetic_enc: true,
max_mem_mgr: None,
}
}
}
impl X86ProjectConfigurable for X86JpegTurboConfig {
fn name(&self) -> &str {
"libjpeg-turbo"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let core = [
"jaricom.c",
"jcapimin.c",
"jcapistd.c",
"jcarith.c",
"jccoefct.c",
"jccolor.c",
"jcdctmgr.c",
"jchuff.c",
"jcinit.c",
"jcmainct.c",
"jcmarker.c",
"jcmaster.c",
"jcomapi.c",
"jcparam.c",
"jcprepct.c",
"jcsample.c",
"jctrans.c",
"jdapimin.c",
"jdapistd.c",
"jdarith.c",
"jdatadst.c",
"jdatasrc.c",
"jdcoefct.c",
"jdcolor.c",
"jddctmgr.c",
"jdhuff.c",
"jdinput.c",
"jdmainct.c",
"jdmarker.c",
"jdmaster.c",
"jdmerge.c",
"jdpostct.c",
"jdsample.c",
"jdtrans.c",
"jerror.c",
"jfdctflt.c",
"jfdctfst.c",
"jfdctint.c",
"jidctflt.c",
"jidctfst.c",
"jidctint.c",
"jmemmgr.c",
"jmemnobs.c",
"jquant1.c",
"jquant2.c",
"jutils.c",
];
let mut files: Vec<PathBuf> = core
.iter()
.map(|s| root.join(s))
.filter(|p| p.exists())
.collect();
let simd_files = match self.simd_level {
X86SIMDLevel::AVX2 | X86SIMDLevel::AVX512F => {
vec![
"jsimd_x86_64.c",
"jccolor-avx2.c",
"jcgray-avx2.c",
"jchuff-avx2.c",
]
}
X86SIMDLevel::SSE2
| X86SIMDLevel::SSE3
| X86SIMDLevel::SSSE3
| X86SIMDLevel::SSE41
| X86SIMDLevel::SSE42
| X86SIMDLevel::AVX => {
vec![
"jsimd_x86_64.c",
"jccolor-sse2.c",
"jcgray-sse2.c",
"jchuff-sse2.c",
]
}
_ => vec![],
};
for f in &simd_files {
let path = root.join("simd").join(f);
if path.exists() {
files.push(path);
}
}
if self.with_turbojpeg {
for f in &["turbojpeg.c", "transupp.c", "jpegtran.c", "rdswitch.c"] {
let path = root.join(f);
if path.exists() {
files.push(path);
}
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![("WITH_SIMD".into(), Some("1".into()))];
if self.with_arithmetic_enc {
defs.push(("C_ARITH_CODING_SUPPORTED".into(), Some("1".into())));
defs.push(("D_ARITH_CODING_SUPPORTED".into(), Some("1".into())));
}
if self.with_12bit {
defs.push(("WITH_12BIT".into(), Some("1".into())));
}
if let Some(max_mem) = self.max_mem_mgr {
defs.push(("MAX_ALLOC_CHUNK".into(), Some(max_mem.to_string())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![root.to_path_buf()];
let simd_dir = root.join("simd");
if simd_dir.exists() {
dirs.push(simd_dir);
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c99".into(),
"-O3".into(), "-Wall".into(),
];
flags.extend(
self.simd_level
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
fn linker_flags(&self) -> Vec<String> {
vec!["-lm".into()]
}
fn libraries(&self) -> Vec<String> {
vec!["m".into()]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
let flags = self.compiler_flags();
let defs = self.defines();
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
let defs_str = defs
.iter()
.map(|(k, v)| match v {
Some(val) => format!("-D{}={}", k, val),
None => format!("-D{}", k),
})
.collect::<Vec<_>>()
.join(" ");
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} {} -c {} -o {}",
flags.join(" "),
defs_str,
src.display(),
obj.display()
),
flags: flags.clone(),
defines: defs.clone(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!(
"{} -outfile test.jpg testimg.bmp",
build_dir.join("cjpeg").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![format!(
"SIMD level: {:?} (SSE2 minimum for x86-64)",
self.simd_level
)]
}
}
pub struct X86OpenSSLConfig {
pub target: String,
pub shared: bool,
pub asm: bool,
pub enable_sse2: bool,
pub no_shared: bool,
pub enabled_features: HashSet<String>,
pub disabled_features: HashSet<String>,
pub version: String,
}
impl Default for X86OpenSSLConfig {
fn default() -> Self {
let mut enabled = HashSet::new();
enabled.insert("tls1_3".into());
enabled.insert("sctp".into());
enabled.insert("srtp".into());
enabled.insert("tls".into());
Self {
target: "linux-x86_64".into(),
shared: true,
asm: true,
enable_sse2: true,
no_shared: false,
enabled_features: enabled,
disabled_features: HashSet::new(),
version: "3.0.0".into(),
}
}
}
impl X86OpenSSLConfig {
pub fn emulate_configure(&self) -> Vec<String> {
let mut args = vec!["./Configure".into(), self.target.clone()];
if self.shared {
args.push("shared".into());
}
if self.no_shared {
args.push("no-shared".into());
}
if !self.asm {
args.push("no-asm".into());
}
if !self.enable_sse2 {
args.push("no-sse2".into());
}
for feat in &self.disabled_features {
args.push(format!("no-{}", feat));
}
for feat in &self.enabled_features {
args.push(format!("enable-{}", feat));
}
args
}
}
impl X86ProjectConfigurable for X86OpenSSLConfig {
fn name(&self) -> &str {
"openssl"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let dirs = ["crypto", "ssl", "apps"];
for dir_name in &dirs {
let dir = root.join(dir_name);
if !dir.exists() {
continue;
}
if let Ok(entries) = fs::read_dir(&dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if ext == "c" || ext == "cc" || ext == "cpp" {
files.push(path);
}
}
}
}
if let Ok(entries) = fs::read_dir(&dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if let Ok(sub_entries) = fs::read_dir(&path) {
for sub in sub_entries.flatten() {
let spath = sub.path();
if let Some(ext) = spath.extension().and_then(|e| e.to_str()) {
if ext == "c" || ext == "cc" {
files.push(spath);
}
}
}
}
}
}
}
}
if self.asm {
let asm_dir = root.join("crypto").join("sha");
if asm_dir.exists() {
if let Ok(entries) = fs::read_dir(&asm_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if ext == "s" || ext == "S" {
files.push(path);
}
}
}
}
}
}
files.sort();
files.dedup();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![("OPENSSL_SUPPRESS_DEPRECATED".into(), None)];
if self.enable_sse2 {
defs.push(("OPENSSL_IA32_SSE2".into(), Some("1".into())));
}
for feat in &self.enabled_features {
let upper = feat.to_uppercase().replace('-', "_");
defs.push((format!("OPENSSL_ENABLE_{}", upper), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.to_path_buf(),
root.join("include"),
root.join("crypto"),
]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c11".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-missing-field-initializers".into(),
"-DOPENSSL_PIC".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
vec!["-ldl".into(), "-lpthread".into(), "-lcrypto".into()]
}
fn libraries(&self) -> Vec<String> {
vec!["dl".into(), "pthread".into(), "crypto".into()]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
let flags = self.compiler_flags();
let defs = self.defines();
for src in &sources {
let rel = src.strip_prefix(root).unwrap_or(src);
let stem = rel.to_string_lossy().replace('/', "_").replace('\\', "_");
let obj = output_dir.join(format!("{}.o", stem.trim_end_matches(".c")));
let defs_str = defs
.iter()
.map(|(k, v)| match v {
Some(val) => format!("-D{}={}", k, val),
None => format!("-D{}", k),
})
.collect::<Vec<_>>()
.join(" ");
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} {} -c {} -o {}",
flags.join(" "),
defs_str,
src.display(),
obj.display()
),
flags: flags.clone(),
defines: defs.clone(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{} version", build_dir.join("openssl").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Make
}
fn notes(&self) -> Vec<String> {
vec![
format!("Configure target: {}", self.target),
format!(
"Assembly optimized: {}",
if self.asm { "yes" } else { "no" }
),
]
}
}
pub struct X86CurlConfig {
pub enable_http: bool,
pub enable_ssl: bool,
pub enable_ftp: bool,
pub enable_http2: bool,
pub enable_http3: bool,
pub enable_cookies: bool,
pub enable_ipv6: bool,
pub enable_unix_sockets: bool,
pub enable_verbose: bool,
pub tls_backend: X86TlsBackend,
pub with_zlib: bool,
pub with_brotli: bool,
pub with_zstd: bool,
pub with_nghttp2: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TlsBackend {
OpenSSL,
GnuTLS,
LibreSSL,
BoringSSL,
BearSSL,
WolfSSL,
SecureTransport,
Schannel,
None,
}
impl Default for X86CurlConfig {
fn default() -> Self {
Self {
enable_http: true,
enable_ssl: true,
enable_ftp: true,
enable_http2: false,
enable_http3: false,
enable_cookies: true,
enable_ipv6: true,
enable_unix_sockets: true,
enable_verbose: true,
tls_backend: X86TlsBackend::OpenSSL,
with_zlib: true,
with_brotli: false,
with_zstd: false,
with_nghttp2: false,
}
}
}
impl X86ProjectConfigurable for X86CurlConfig {
fn name(&self) -> &str {
"curl"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let lib_dir = root.join("lib");
let vtls_dir = root.join("lib").join("vtls");
let vauth_dir = root.join("lib").join("vauth");
let vquic_dir = root.join("lib").join("vquic");
let vssh_dir = root.join("lib").join("vssh");
let mut files = Vec::new();
let core = [
"altsvc.c",
"amigaos.c",
"asyn-ares.c",
"asyn-thread.c",
"base64.c",
"bufq.c",
"bufref.c",
"c-hyper.c",
"cf-h1-proxy.c",
"cf-h2-proxy.c",
"cf-haproxy.c",
"cf-https-connect.c",
"cf-socket.c",
"cfilters.c",
"conncache.c",
"connect.c",
"content_encoding.c",
"cookie.c",
"curl_addrinfo.c",
"curl_ctype.c",
"curl_des.c",
"curl_endian.c",
"curl_fnmatch.c",
"curl_get_line.c",
"curl_gethostname.c",
"curl_gssapi.c",
"curl_memrchr.c",
"curl_multibyte.c",
"curl_ntlm_core.c",
"curl_ntlm_wb.c",
"curl_path.c",
"curl_range.c",
"curl_rtmp.c",
"curl_sasl.c",
"curl_sspi.c",
"curl_threads.c",
"dict.c",
"doh.c",
"dynbuf.c",
"dynhds.c",
"easy.c",
"easygetopt.c",
"easyoptions.c",
"escape.c",
"file.c",
"fileinfo.c",
"fopen.c",
"formdata.c",
"ftp.c",
"ftplistparser.c",
"getenv.c",
"getinfo.c",
"gopher.c",
"hash.c",
"headers.c",
"hmac.c",
"hostasyn.c",
"hostip.c",
"hostip4.c",
"hostip6.c",
"hostsyn.c",
"hsts.c",
"http.c",
"http1.c",
"http2.c",
"http_aws_sigv4.c",
"http_chunks.c",
"http_digest.c",
"http_negotiate.c",
"http_ntlm.c",
"http_proxy.c",
"idn.c",
"if2ip.c",
"imap.c",
"inet_ntop.c",
"inet_pton.c",
"krb5.c",
"ldap.c",
"llist.c",
"macos.c",
"md4.c",
"md5.c",
"memdebug.c",
"mime.c",
"mprintf.c",
"mqtt.c",
"multi.c",
"netrc.c",
"nonblock.c",
"noproxy.c",
"openldap.c",
"parsedate.c",
"pingpong.c",
"pop3.c",
"progress.c",
"psl.c",
"rand.c",
"rename.c",
"rtsp.c",
"select.c",
"sendf.c",
"setopt.c",
"sha256.c",
"share.c",
"slist.c",
"smb.c",
"smtp.c",
"socketpair.c",
"socks.c",
"socks_gssapi.c",
"socks_sspi.c",
"speedcheck.c",
"splay.c",
"strcase.c",
"strdup.c",
"strerror.c",
"strtok.c",
"strtoofft.c",
"system_win32.c",
"telnet.c",
"tftp.c",
"timeval.c",
"transfer.c",
"url.c",
"urlapi.c",
"version.c",
"warnless.c",
"wildcard.c",
];
for f in &core {
let p = lib_dir.join(f);
if p.exists() {
files.push(p);
}
}
if self.enable_ssl {
let vtls_files = match self.tls_backend {
X86TlsBackend::OpenSSL => vec!["openssl.c", "vtls.c", "keylog.c", "x509asn1.c"],
X86TlsBackend::GnuTLS => vec!["gtls.c", "vtls.c", "keylog.c"],
_ => vec!["vtls.c", "keylog.c"],
};
for f in &vtls_files {
let p = vtls_dir.join(f);
if p.exists() {
files.push(p);
}
}
}
for f in &[
"cleartext.c",
"cram.c",
"digest.c",
"digest_sspi.c",
"gsasl.c",
"ntlm.c",
"ntlm_sspi.c",
"oauth2.c",
"spnego_gssapi.c",
"spnego_sspi.c",
"vauth.c",
] {
let p = vauth_dir.join(f);
if p.exists() {
files.push(p);
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("CURL_STATICLIB".into(), None),
("BUILDING_LIBCURL".into(), None),
];
if self.enable_http {
defs.push(("USE_HTTP".into(), Some("1".into())));
}
if self.enable_ssl {
defs.push(("USE_SSL".into(), Some("1".into())));
}
if self.enable_http2 {
defs.push(("USE_NGHTTP2".into(), Some("1".into())));
}
if self.enable_http3 {
defs.push(("USE_NGTCP2".into(), Some("1".into())));
}
if self.enable_ipv6 {
defs.push(("ENABLE_IPV6".into(), Some("1".into())));
}
if self.enable_unix_sockets {
defs.push(("USE_UNIX_SOCKETS".into(), Some("1".into())));
}
if self.with_zlib {
defs.push(("HAVE_LIBZ".into(), Some("1".into())));
}
if self.with_brotli {
defs.push(("HAVE_BROTLI".into(), Some("1".into())));
}
if self.with_zstd {
defs.push(("HAVE_ZSTD".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![root.join("include"), root.join("lib")];
if self.with_zlib {
dirs.push(PathBuf::from("/usr/include"));
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.with_zlib {
flags.push("-lz".into());
}
if self.enable_ssl {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
if self.with_nghttp2 {
flags.push("-lnghttp2".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = Vec::new();
if self.with_zlib {
libs.push("z".into());
}
if self.enable_ssl {
libs.push("ssl".into());
libs.push("crypto".into());
}
libs
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
let flags = self.compiler_flags();
let defs = self.defines();
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
let defs_str = defs
.iter()
.map(|(k, v)| match v {
Some(val) => format!("-D{}={}", k, val),
None => format!("-D{}", k),
})
.collect::<Vec<_>>()
.join(" ");
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} {} -c {} -o {}",
flags.join(" "),
defs_str,
src.display(),
obj.display()
),
flags: flags.clone(),
defines: defs.clone(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{} --version", build_dir.join("curl").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
let mut notes = vec![format!("TLS backend: {:?}", self.tls_backend)];
if self.enable_http2 {
notes.push("HTTP/2 enabled (requires nghttp2)".into());
}
if self.enable_http3 {
notes.push("HTTP/3 enabled (requires ngtcp2 + nghttp3)".into());
}
notes
}
}
pub struct X86NginxConfig {
pub modules: Vec<X86NginxModule>,
pub http: bool,
pub mail: bool,
pub stream: bool,
pub with_ssl: bool,
pub openssl_include: Option<PathBuf>,
pub openssl_lib: Option<PathBuf>,
pub zlib_include: Option<PathBuf>,
pub pcre_include: Option<PathBuf>,
pub with_debug: bool,
pub with_threads: bool,
pub with_file_aio: bool,
pub with_ipv6: bool,
}
#[derive(Debug, Clone)]
pub struct X86NginxModule {
pub name: String,
pub path: PathBuf,
pub enabled: bool,
pub defines: Vec<(String, Option<String>)>,
pub includes: Vec<PathBuf>,
}
impl Default for X86NginxConfig {
fn default() -> Self {
Self {
modules: vec![
X86NginxModule {
name: "ngx_http_module".into(),
path: PathBuf::from("src/http/modules"),
enabled: true,
defines: vec![],
includes: vec![],
},
X86NginxModule {
name: "ngx_http_ssl_module".into(),
path: PathBuf::from("src/http/modules"),
enabled: true,
defines: vec![("NGX_HTTP_SSL".into(), None)],
includes: vec![],
},
X86NginxModule {
name: "ngx_http_gzip_module".into(),
path: PathBuf::from("src/http/modules"),
enabled: true,
defines: vec![],
includes: vec![],
},
],
http: true,
mail: false,
stream: false,
with_ssl: true,
openssl_include: None,
openssl_lib: None,
zlib_include: None,
pcre_include: None,
with_debug: false,
with_threads: true,
with_file_aio: false,
with_ipv6: true,
}
}
}
impl X86ProjectConfigurable for X86NginxConfig {
fn name(&self) -> &str {
"nginx"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let core_dir = root.join("src").join("core");
let core_files = [
"nginx.c",
"ngx_array.c",
"ngx_buf.c",
"ngx_conf_file.c",
"ngx_connection.c",
"ngx_cpuinfo.c",
"ngx_crc32.c",
"ngx_crypt.c",
"ngx_cycle.c",
"ngx_file.c",
"ngx_hash.c",
"ngx_inet.c",
"ngx_list.c",
"ngx_log.c",
"ngx_md5.c",
"ngx_module.c",
"ngx_murmurhash.c",
"ngx_output_chain.c",
"ngx_palloc.c",
"ngx_parse.c",
"ngx_parse_time.c",
"ngx_proxy_protocol.c",
"ngx_queue.c",
"ngx_radix_tree.c",
"ngx_rbtree.c",
"ngx_regex.c",
"ngx_resolver.c",
"ngx_rwlock.c",
"ngx_sha1.c",
"ngx_shmtx.c",
"ngx_slab.c",
"ngx_spinlock.c",
"ngx_string.c",
"ngx_syslog.c",
"ngx_times.c",
];
for f in &core_files {
let p = core_dir.join(f);
if p.exists() {
files.push(p);
}
}
let event_dir = root.join("src").join("event");
for f in &[
"ngx_event.c",
"ngx_event_accept.c",
"ngx_event_connect.c",
"ngx_event_pipe.c",
"ngx_event_posted.c",
"ngx_event_timer.c",
"ngx_event_udp.c",
] {
let p = event_dir.join(f);
if p.exists() {
files.push(p);
}
}
for f in &[
"ngx_epoll_module.c",
"ngx_select_module.c",
"ngx_poll_module.c",
"ngx_devpoll_module.c",
"ngx_eventport_module.c",
"ngx_kqueue_module.c",
] {
let p = event_dir.join("modules").join(f);
if p.exists() {
files.push(p);
}
}
if self.http {
let http_dir = root.join("src").join("http");
for f in &[
"ngx_http.c",
"ngx_http_core_module.c",
"ngx_http_header_filter_module.c",
"ngx_http_parse.c",
"ngx_http_postpone_filter_module.c",
"ngx_http_request.c",
"ngx_http_request_body.c",
"ngx_http_script.c",
"ngx_http_special_response.c",
"ngx_http_upstream.c",
"ngx_http_upstream_round_robin.c",
"ngx_http_variables.c",
"ngx_http_write_filter_module.c",
] {
let p = http_dir.join(f);
if p.exists() {
files.push(p);
}
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("NGINX_VER".into(), Some(r#""1.25.0""#.into())),
("NGX_PTR_SIZE".into(), Some("8".into())),
("NGX_HAVE_LITTLE_ENDIAN".into(), Some("1".into())),
];
if self.with_debug {
defs.push(("NGX_DEBUG".into(), Some("1".into())));
}
if self.with_threads {
defs.push(("NGX_THREADS".into(), Some("1".into())));
}
if self.with_ipv6 {
defs.push(("NGX_HAVE_INET6".into(), Some("1".into())));
}
if self.http {
defs.push(("NGX_HTTP".into(), Some("1".into())));
}
for module in &self.modules {
if module.enabled {
for (k, v) in &module.defines {
defs.push((k.clone(), v.clone()));
}
}
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.join("src").join("core"),
root.join("src").join("event"),
root.join("src").join("http"),
root.join("objs"),
];
if let Some(ref zlib) = self.zlib_include {
dirs.push(zlib.clone());
}
if let Some(ref pcre) = self.pcre_include {
dirs.push(pcre.clone());
}
if let Some(ref ossl) = self.openssl_include {
dirs.push(ossl.clone());
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c11".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-pipe".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.with_ssl {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
flags.push("-lz".into());
flags.push("-lpcre".into());
flags.push("-lpthread".into());
flags.push("-lcrypt".into());
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["z".into(), "pcre".into(), "pthread".into(), "crypt".into()];
if self.with_ssl {
libs.push("ssl".into());
libs.push("crypto".into());
}
libs
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} -c {} -o {}",
self.compiler_flags().join(" "),
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{} -t", build_dir.join("nginx").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Custom
}
fn notes(&self) -> Vec<String> {
vec![
format!(
"Modules enabled: {}",
self.modules.iter().filter(|m| m.enabled).count()
),
"nginx uses a custom configure+make build system".into(),
]
}
}
pub struct X86RedisConfig {
pub use_jemalloc: bool,
pub build_tls: bool,
pub with_systemd: bool,
pub opt_flags: String,
pub malloc: X86RedisMalloc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RedisMalloc {
Jemalloc,
Libc,
Tcmalloc,
}
impl Default for X86RedisConfig {
fn default() -> Self {
Self {
use_jemalloc: true,
build_tls: false,
with_systemd: false,
opt_flags: "-O2".into(),
malloc: X86RedisMalloc::Jemalloc,
}
}
}
impl X86ProjectConfigurable for X86RedisConfig {
fn name(&self) -> &str {
"redis"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src");
let sources = [
"server.c",
"anet.c",
"aof.c",
"bio.c",
"bitops.c",
"blocked.c",
"call_reply.c",
"childinfo.c",
"cli_common.c",
"cluster.c",
"config.c",
"connection.c",
"crc16.c",
"crcspeed.c",
"db.c",
"debug.c",
"defrag.c",
"dict.c",
"endianconv.c",
"eval.c",
"evict.c",
"expire.c",
"function_lua.c",
"functions.c",
"geo.c",
"hyperloglog.c",
"intset.c",
"kvstore.c",
"latency.c",
"lazyfree.c",
"listpack.c",
"localtime.c",
"lolwut.c",
"lolwut5.c",
"lolwut6.c",
"lzf_c.c",
"lzf_d.c",
"memtest.c",
"module.c",
"monotonic.c",
"mt19937-64.c",
"multi.c",
"networking.c",
"notify.c",
"object.c",
"pqsort.c",
"pubsub.c",
"quicklist.c",
"rand.c",
"rax.c",
"rdb.c",
"redis-benchmark.c",
"redis-check-aof.c",
"redis-check-rdb.c",
"redis-cli.c",
"release.c",
"replication.c",
"resp_parser.c",
"rio.c",
"script.c",
"script_lua.c",
"sds.c",
"sentinel.c",
"server.c",
"setcpuaffinity.c",
"setproctitle.c",
"sha1.c",
"sha256.c",
"siphash.c",
"slowlog.c",
"sockunion.c",
"sort.c",
"sparkline.c",
"syncio.c",
"syscheck.c",
"t_hash.c",
"t_list.c",
"t_set.c",
"t_string.c",
"t_zset.c",
"timeout.c",
"tls.c",
"tracking.c",
"unix.c",
"util.c",
"ziplist.c",
"zipmap.c",
"zmalloc.c",
];
sources
.iter()
.map(|s| src_dir.join(s))
.filter(|p| p.exists())
.collect()
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = Vec::new();
if self.use_jemalloc {
defs.push(("USE_JEMALLOC".into(), Some("1".into())));
}
if self.build_tls {
defs.push(("USE_OPENSSL".into(), Some("1".into())));
}
if self.with_systemd {
defs.push(("HAVE_LIBSYSTEMD".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.join("src"),
root.join("deps").join("hiredis"),
root.join("deps").join("linenoise"),
root.join("deps").join("lua").join("src"),
];
if self.use_jemalloc {
dirs.push(root.join("deps").join("jemalloc").join("include"));
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".into(),
self.opt_flags.clone(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-sign-compare".into(),
"-Wno-unused-parameter".into(),
"-fno-omit-frame-pointer".into(),
];
flags.push("-march=native".into());
flags
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec!["-lm".into(), "-lpthread".into(), "-ldl".into()];
if self.build_tls {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into(), "pthread".into(), "dl".into()];
if self.build_tls {
libs.push("ssl".into());
libs.push("crypto".into());
}
libs
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} -c {} -o {}",
self.compiler_flags().join(" "),
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!(
"{} --version",
build_dir.join("redis-server").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Make
}
fn notes(&self) -> Vec<String> {
vec![
format!("Malloc: {:?}", self.malloc),
"Redis uses a simple Makefile build with no autotools".into(),
]
}
}
pub struct X86LuaConfig {
pub lua_version: String,
pub build_interpreter: bool,
pub build_compiler: bool,
pub with_readline: bool,
pub with_dlopen: bool,
pub use_32bit_numbers: bool,
pub float_width: X86LuaFloat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86LuaFloat {
Float,
Double,
LongDouble,
}
impl Default for X86LuaConfig {
fn default() -> Self {
Self {
lua_version: "5.4".into(),
build_interpreter: true,
build_compiler: true,
with_readline: true,
with_dlopen: true,
use_32bit_numbers: false,
float_width: X86LuaFloat::Double,
}
}
}
impl X86ProjectConfigurable for X86LuaConfig {
fn name(&self) -> &str {
"lua"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src");
let mut files: Vec<PathBuf> = [
"lapi.c",
"lauxlib.c",
"lbaselib.c",
"lcode.c",
"lcorolib.c",
"lctype.c",
"ldblib.c",
"ldebug.c",
"ldo.c",
"ldump.c",
"lfunc.c",
"lgc.c",
"linit.c",
"liolib.c",
"llex.c",
"lmathlib.c",
"lmem.c",
"loadlib.c",
"lobject.c",
"lopcodes.c",
"loslib.c",
"lparser.c",
"lstate.c",
"lstring.c",
"lstrlib.c",
"ltable.c",
"ltablib.c",
"ltm.c",
"lundump.c",
"lutf8lib.c",
"lvm.c",
"lzio.c",
]
.iter()
.map(|s| src_dir.join(s))
.filter(|p| p.exists())
.collect();
if self.build_interpreter {
let lua_c = root.join("src").join("lua.c");
if lua_c.exists() {
files.push(lua_c);
}
}
if self.build_compiler {
let luac_c = root.join("src").join("luac.c");
if luac_c.exists() {
files.push(luac_c);
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
(
format!("LUA_VERSION_{}", self.lua_version.replace('.', "_")),
None,
),
("LUA_COMPAT_5_3".into(), None),
("LUA_COMPAT_ALL".into(), None),
("LUA_USE_LINUX".into(), None),
];
if self.use_32bit_numbers {
defs.push(("LUA_32BITS".into(), None));
}
if self.with_dlopen {
defs.push(("LUA_USE_DLOPEN".into(), None));
}
match self.float_width {
X86LuaFloat::Float => defs.push(("LUA_FLOAT_TYPE".into(), Some("float".into()))),
X86LuaFloat::Double => {}
X86LuaFloat::LongDouble => defs.push(("LUA_FLOAT_LONGDOUBLE".into(), None)),
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.join("src")]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-Wno-missing-field-initializers".into(),
"-DLUA_COMPAT_5_3".into(),
"-DLUA_USE_LINUX".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec!["-lm".into()];
if self.with_readline {
flags.push("-lreadline".into());
}
if self.with_dlopen {
flags.push("-ldl".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into()];
if self.with_readline {
libs.push("readline".into());
}
if self.with_dlopen {
libs.push("dl".into());
}
libs
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} -c {} -o {}",
self.compiler_flags().join(" "),
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!(
"{} -e 'print(\"hello\")'",
build_dir.join("lua").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Make
}
fn notes(&self) -> Vec<String> {
vec![
format!(
"Lua {} ANSI C build. Float: {:?}",
self.lua_version, self.float_width
),
"Lua uses a simple Makefile with no dependencies beyond libc".into(),
]
}
}
pub struct X86JsonCConfig {
pub enable_threading: bool,
pub enable_rdrand: bool,
pub oldname_compat: bool,
pub disable_bsd_behavior: bool,
}
impl Default for X86JsonCConfig {
fn default() -> Self {
Self {
enable_threading: true,
enable_rdrand: true,
oldname_compat: false,
disable_bsd_behavior: false,
}
}
}
impl X86ProjectConfigurable for X86JsonCConfig {
fn name(&self) -> &str {
"json-c"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let sources = [
"arraylist.c",
"debug.c",
"json_c_version.c",
"json_object.c",
"json_object_iterator.c",
"json_pointer.c",
"json_tokener.c",
"json_util.c",
"json_visit.c",
"linkhash.c",
"printbuf.c",
"random_seed.c",
"strerror_override.c",
];
sources
.iter()
.map(|s| root.join(s))
.filter(|p| p.exists())
.collect()
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = Vec::new();
if self.enable_threading {
defs.push(("ENABLE_THREADING".into(), Some("1".into())));
}
if self.enable_rdrand {
defs.push(("ENABLE_RDRAND".into(), Some("1".into())));
}
if self.oldname_compat {
defs.push(("JSON_C_OLDNAME_COMPAT".into(), None));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.to_path_buf(), root.join("..")]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec!["-lm".into()];
if self.enable_threading {
flags.push("-lpthread".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into()];
if self.enable_threading {
libs.push("pthread".into());
}
libs
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"cc {} -c {} -o {}",
self.compiler_flags().join(" "),
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!(
"{} test1.json",
build_dir.join("test_util").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
}
pub struct X86UthashConfig;
impl X86ProjectConfigurable for X86UthashConfig {
fn name(&self) -> &str {
"uthash"
}
fn sources(&self, _root: &Path) -> Vec<PathBuf> {
Vec::new() }
fn defines(&self) -> Vec<(String, Option<String>)> {
vec![("UTHASH_VERSION".into(), Some(r#""2.3.0""#.into()))]
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.join("src"), root.join("include")]
}
fn compiler_flags(&self) -> Vec<String> {
vec!["-std=c99".into()]
}
fn linker_flags(&self) -> Vec<String> {
vec![]
}
fn libraries(&self) -> Vec<String> {
vec![]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
}
}
fn test_commands(&self, _build_dir: &Path) -> Vec<String> {
vec!["# uthash is header-only, test via include".into()]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Unknown
}
fn notes(&self) -> Vec<String> {
vec![
"uthash is a header-only library: just #include \"uthash.h\"".into(),
"No compilation required; headers provide macros for hash tables".into(),
]
}
}
pub struct X86StbConfig {
pub libraries: Vec<X86StbLibrary>,
}
#[derive(Debug, Clone)]
pub enum X86StbLibrary {
Image,
ImageWrite,
ImageResize,
TrueType,
RectanglePack,
VoxelRender,
Perlin,
EasyFont,
TextEdit,
TileMapEditor,
HerringboneWangTile,
}
impl X86StbLibrary {
pub fn header_name(&self) -> &'static str {
match self {
Self::Image => "stb_image.h",
Self::ImageWrite => "stb_image_write.h",
Self::ImageResize => "stb_image_resize.h",
Self::TrueType => "stb_truetype.h",
Self::RectanglePack => "stb_rect_pack.h",
Self::VoxelRender => "stb_voxel_render.h",
Self::Perlin => "stb_perlin.h",
Self::EasyFont => "stb_easy_font.h",
Self::TextEdit => "stb_textedit.h",
Self::TileMapEditor => "stb_tilemap_editor.h",
Self::HerringboneWangTile => "stb_herringbone_wang_tile.h",
}
}
pub fn implementation_defines(&self) -> Vec<&'static str> {
match self {
Self::Image => vec!["STB_IMAGE_IMPLEMENTATION"],
Self::ImageWrite => vec!["STB_IMAGE_WRITE_IMPLEMENTATION"],
Self::ImageResize => vec!["STB_IMAGE_RESIZE_IMPLEMENTATION"],
Self::TrueType => vec!["STB_TRUETYPE_IMPLEMENTATION"],
Self::RectanglePack => vec!["STB_RECT_PACK_IMPLEMENTATION"],
Self::VoxelRender => vec!["STB_VOXEL_RENDER_IMPLEMENTATION"],
Self::Perlin => vec!["STB_PERLIN_IMPLEMENTATION"],
Self::EasyFont => vec!["STB_EASY_FONT_IMPLEMENTATION"],
Self::TextEdit => vec!["STB_TEXTEDIT_IMPLEMENTATION"],
Self::TileMapEditor => vec!["STB_TILEMAP_EDITOR_IMPLEMENTATION"],
Self::HerringboneWangTile => vec!["STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION"],
}
}
}
impl Default for X86StbConfig {
fn default() -> Self {
Self {
libraries: vec![X86StbLibrary::Image, X86StbLibrary::ImageWrite],
}
}
}
impl X86ProjectConfigurable for X86StbConfig {
fn name(&self) -> &str {
"stb"
}
fn sources(&self, _root: &Path) -> Vec<PathBuf> {
Vec::new() }
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = Vec::new();
for lib in &self.libraries {
for imp_def in lib.implementation_defines() {
defs.push((imp_def.to_string(), None));
}
}
defs.push(("STBI_NO_SIMD".into(), None)); defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.to_path_buf()]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c99".into(),
"-O2".into(),
"-Wall".into(),
"-Wno-unused-function".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
vec!["-lm".into()]
}
fn libraries(&self) -> Vec<String> {
vec!["m".into()]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
}
}
fn test_commands(&self, _build_dir: &Path) -> Vec<String> {
vec!["# stb is header-only, test via implementation defines".into()]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Unknown
}
fn notes(&self) -> Vec<String> {
let lib_count = self.libraries.len();
vec![
"stb libraries are single-header: #define IMPLEMENTATION before include".into(),
format!("{} stb libraries configured", lib_count),
]
}
}
pub struct X86DoctestConfig {
pub cxx_standard: String,
pub build_tests: bool,
}
impl Default for X86DoctestConfig {
fn default() -> Self {
Self {
cxx_standard: "c++17".into(),
build_tests: true,
}
}
}
impl X86ProjectConfigurable for X86DoctestConfig {
fn name(&self) -> &str {
"doctest"
}
fn sources(&self, _root: &Path) -> Vec<PathBuf> {
Vec::new() }
fn defines(&self) -> Vec<(String, Option<String>)> {
vec![
("DOCTEST_CONFIG_IMPLEMENT".into(), None),
("DOCTEST_CONFIG_SUPER_FAST_ASSERTS".into(), None),
]
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.join("doctest"),
root.join("single_include"),
root.to_path_buf(),
]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
format!("-std={}", self.cxx_standard),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
vec![]
}
fn libraries(&self) -> Vec<String> {
vec![]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
}
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{} --version", build_dir.join("tests").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![
format!(
"doctest is header-only. C++ standard: {}",
self.cxx_standard
),
"Main header: doctest/doctest.h or single_include/doctest.h".into(),
]
}
}
pub struct X86FmtlibConfig {
pub cxx_standard: String,
pub header_only: bool,
pub enable_compile_checks: bool,
pub with_wchar: bool,
pub with_unicode: bool,
pub with_fp128: bool,
}
impl Default for X86FmtlibConfig {
fn default() -> Self {
Self {
cxx_standard: "c++17".into(),
header_only: false,
enable_compile_checks: true,
with_wchar: true,
with_unicode: true,
with_fp128: false,
}
}
}
impl X86ProjectConfigurable for X86FmtlibConfig {
fn name(&self) -> &str {
"fmtlib"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
if self.header_only {
return Vec::new();
}
let mut files = Vec::new();
for name in &[
"format.cc",
"os.cc",
"fmt/format.cc",
"fmt/os.cc",
"src/format.cc",
"src/os.cc",
] {
let path = root.join(name);
if path.exists() {
files.push(path);
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = Vec::new();
if self.header_only {
defs.push(("FMT_HEADER_ONLY".into(), Some("1".into())));
}
if self.enable_compile_checks {
defs.push(("FMT_ENABLE_COMPILE_TIME_CHECKS".into(), Some("1".into())));
}
if self.with_wchar {
defs.push(("FMT_USE_WCHAR".into(), Some("1".into())));
}
if self.with_unicode {
defs.push(("FMT_UNICODE".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.join("include"), root.to_path_buf()]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
format!("-std={}", self.cxx_standard),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
vec![]
}
fn libraries(&self) -> Vec<String> {
vec![]
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"c++ {} -c {} -o {}",
self.compiler_flags().join(" "),
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{}", build_dir.join("fmt-test").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![format!(
"fmt C++ library, standard: {}, header-only: {}",
self.cxx_standard, self.header_only
)]
}
}
pub struct X86SpdlogConfig {
pub cxx_standard: String,
pub header_only: bool,
pub enable_async: bool,
pub enable_backtrace: bool,
pub enable_wchar: bool,
pub enable_syslog: bool,
pub use_external_fmt: bool,
pub compile_library: bool,
}
impl Default for X86SpdlogConfig {
fn default() -> Self {
Self {
cxx_standard: "c++17".into(),
header_only: true,
enable_async: false,
enable_backtrace: true,
enable_wchar: false,
enable_syslog: false,
use_external_fmt: false,
compile_library: false,
}
}
}
impl X86ProjectConfigurable for X86SpdlogConfig {
fn name(&self) -> &str {
"spdlog"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
if self.header_only && !self.compile_library {
return Vec::new();
}
let mut files = Vec::new();
for name in &["src/spdlog.cpp", "spdlog.cpp", "src/cfg.cpp"] {
let path = root.join(name);
if path.exists() {
files.push(path);
}
}
if self.enable_async {
for name in &["src/async.cpp", "async.cpp"] {
let path = root.join(name);
if path.exists() {
files.push(path);
}
}
}
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = Vec::new();
if self.header_only {
defs.push(("SPDLOG_HEADER_ONLY".into(), Some("1".into())));
}
if self.compile_library {
defs.push(("SPDLOG_COMPILED_LIB".into(), Some("1".into())));
}
if self.enable_async {
defs.push((
"SPDLOG_ACTIVE_LEVEL".into(),
Some("SPDLOG_LEVEL_TRACE".into()),
));
}
if self.enable_wchar {
defs.push(("SPDLOG_WCHAR_TO_UTF8_SUPPORT".into(), Some("1".into())));
}
if self.enable_syslog {
defs.push(("SPDLOG_ENABLE_SYSLOG".into(), Some("1".into())));
}
if self.use_external_fmt {
defs.push(("SPDLOG_FMT_EXTERNAL".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![root.join("include"), root.to_path_buf()]
}
fn compiler_flags(&self) -> Vec<String> {
vec![
format!("-std={}", self.cxx_standard),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
if self.enable_async {
flags.push("-lpthread".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = Vec::new();
if self.enable_async {
libs.push("pthread".into());
}
libs
}
fn compile_database(&self, root: &Path, output_dir: &Path) -> X86CompilationDatabase {
let mut db = X86CompilationDatabase {
project_name: self.name().to_string(),
entries: Vec::new(),
build_system: self.build_system(),
};
let sources = self.sources(root);
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
db.entries.push(X86CompileEntry {
directory: root.to_path_buf(),
source_file: src.clone(),
output_file: obj,
command: format!(
"c++ {} -c {} -o {}",
self.compiler_flags().join(" "),
src.display(),
obj.display()
),
flags: self.compiler_flags(),
defines: self.defines(),
});
}
db
}
fn test_commands(&self, build_dir: &Path) -> Vec<String> {
vec![format!("{}", build_dir.join("spdlog-test").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![format!(
"spdlog C++ logging, mode: {}",
if self.header_only {
"header-only"
} else {
"compiled"
}
)]
}
}
pub struct X86BuildRunner {
pub build_dir: PathBuf,
pub parallel_jobs: usize,
pub verbose: bool,
pub cache: X86BuildCache,
pub errors: Vec<X86CompileDiagnostic>,
pub warnings: Vec<X86CompileDiagnostic>,
pub total_files: usize,
pub succeeded: usize,
pub failed: usize,
}
#[derive(Debug, Clone)]
pub struct X86BuildCache {
pub cache_dir: PathBuf,
pub enabled: bool,
pub max_size: u64,
pub current_size: u64,
pub hits: u64,
pub misses: u64,
cached_objects: HashMap<String, X86CachedObject>,
}
#[derive(Debug, Clone)]
pub struct X86CachedObject {
pub source_file: PathBuf,
pub object_file: PathBuf,
pub source_hash: String,
pub compilation_flags_hash: String,
pub timestamp: u64,
pub size: u64,
}
impl X86BuildCache {
pub fn new(cache_dir: &Path) -> Self {
fs::create_dir_all(cache_dir).ok();
Self {
cache_dir: cache_dir.to_path_buf(),
enabled: true,
max_size: DEFAULT_CACHE_MAX_SIZE,
current_size: 0,
hits: 0,
misses: 0,
cached_objects: HashMap::new(),
}
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn enable(&mut self) {
self.enabled = true;
}
fn compute_hash(data: &[u8]) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
format!("{:016x}", hasher.finish())
}
pub fn lookup(&mut self, source: &Path, flags: &[String]) -> Option<PathBuf> {
if !self.enabled {
self.misses += 1;
return None;
}
let source_hash = match fs::read(source) {
Ok(data) => Self::compute_hash(&data),
Err(_) => {
self.misses += 1;
return None;
}
};
let flags_hash = Self::compute_hash(flags.join(" ").as_bytes());
let key = format!("{}:{}", source_hash, flags_hash);
if let Some(cached) = self.cached_objects.get(&key) {
if cached.object_file.exists() {
self.hits += 1;
return Some(cached.object_file.clone());
}
}
self.misses += 1;
None
}
pub fn store(&mut self, source: &Path, object: &Path, flags: &[String]) {
if !self.enabled {
return;
}
let source_hash = match fs::read(source) {
Ok(data) => Self::compute_hash(&data),
Err(_) => return,
};
let flags_hash = Self::compute_hash(flags.join(" ").as_bytes());
let key = format!("{}:{}", source_hash, flags_hash);
if let Ok(size) = fs::metadata(object).map(|m| m.len()) {
self.cached_objects.insert(
key,
X86CachedObject {
source_file: source.to_path_buf(),
object_file: object.to_path_buf(),
source_hash,
compilation_flags_hash: flags_hash,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
size,
},
);
self.current_size += size;
}
}
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
0.0
} else {
(self.hits as f64) / (total as f64)
}
}
pub fn clear(&mut self) {
self.cached_objects.clear();
self.current_size = 0;
if let Ok(entries) = fs::read_dir(&self.cache_dir) {
for entry in entries.flatten() {
let _ = fs::remove_file(entry.path());
}
}
}
pub fn stats(&self) -> String {
format!(
"Cache: {} hits / {} misses ({:.1}% hit rate), {} bytes used / {} bytes max",
self.hits,
self.misses,
self.hit_rate() * 100.0,
self.current_size,
self.max_size,
)
}
}
impl X86BuildRunner {
pub fn new(build_dir: &Path, parallel_jobs: usize, verbose: bool) -> Self {
let cache = X86BuildCache::new(&build_dir.join(DEFAULT_CACHE_DIR));
Self {
build_dir: build_dir.to_path_buf(),
parallel_jobs: if parallel_jobs == 0 {
num_cpus::get().max(1)
} else {
parallel_jobs
},
verbose,
cache,
errors: Vec::new(),
warnings: Vec::new(),
total_files: 0,
succeeded: 0,
failed: 0,
}
}
pub fn compile_project(
&mut self,
sources: &[PathBuf],
includes: &[PathBuf],
defines: &[(String, Option<String>)],
compiler_flags: &[String],
options: &X86CompileOptions,
) -> Result<Vec<X86BuildResult>, String> {
let start = Instant::now();
let mut base_flags: Vec<String> = Vec::new();
for inc in includes {
base_flags.push(format!("-I{}", inc.display()));
}
for (name, value) in defines {
match value {
Some(v) => base_flags.push(format!("-D{}={}", name, v)),
None => base_flags.push(format!("-D{}", name)),
}
}
base_flags.extend(compiler_flags.iter().cloned());
let total = sources.len();
let results = Arc::new(Mutex::new(Vec::with_capacity(total)));
let errors = Arc::new(Mutex::new(Vec::new()));
let warnings = Arc::new(Mutex::new(Vec::new()));
let succeeded = Arc::new(AtomicUsize::new(0));
let failed = Arc::new(AtomicUsize::new(0));
let chunk_size = (total / self.parallel_jobs).max(1);
let mut handles = Vec::new();
for chunk in sources.chunks(chunk_size) {
let chunk: Vec<PathBuf> = chunk.to_vec();
let build_dir = self.build_dir.clone();
let base_flags = base_flags.clone();
let verbose = self.verbose;
let results = Arc::clone(&results);
let errors = Arc::clone(&errors);
let warnings = Arc::clone(&warnings);
let succeeded = Arc::clone(&succeeded);
let failed = Arc::clone(&failed);
let handle = std::thread::spawn(move || {
for source in &chunk {
let result = Self::compile_single(source, &build_dir, &base_flags, verbose);
if result.success {
succeeded.fetch_add(1, Ordering::Relaxed);
} else {
failed.fetch_add(1, Ordering::Relaxed);
}
if let Ok(mut errs) = errors.lock() {
errs.extend(result.errors.clone());
}
if let Ok(mut warns) = warnings.lock() {
warns.extend(result.warnings.clone());
}
if let Ok(mut res) = results.lock() {
res.push(result);
}
}
});
handles.push(handle);
}
for handle in handles {
handle
.join()
.map_err(|_| "Thread panicked during build".to_string())?;
}
let final_results = Arc::try_unwrap(results)
.map_err(|_| "Failed to unwrap build results".to_string())?
.into_inner()
.map_err(|_| "Failed to unwrap build results mutex".to_string())?;
let final_errors = Arc::try_unwrap(errors)
.ok()
.and_then(|m| m.into_inner().ok())
.unwrap_or_default();
let final_warnings = Arc::try_unwrap(warnings)
.ok()
.and_then(|m| m.into_inner().ok())
.unwrap_or_default();
self.errors = final_errors;
self.warnings = final_warnings;
self.total_files = final_results.len();
self.succeeded = succeeded.load(Ordering::Relaxed);
self.failed = failed.load(Ordering::Relaxed);
if self.verbose {
let elapsed = start.elapsed();
eprintln!(
"[X86BuildRunner] Compiled {} sources in {:.2}s ({} ok, {} failed, {} jobs)",
self.total_files,
elapsed.as_secs_f64(),
self.succeeded,
self.failed,
self.parallel_jobs,
);
}
Ok(final_results)
}
fn compile_single(
source: &Path,
build_dir: &Path,
base_flags: &[String],
verbose: bool,
) -> X86BuildResult {
let start = Instant::now();
let stem = source.file_stem().unwrap_or_default().to_string_lossy();
let object_file = build_dir.join(format!("{}.o", stem));
let mut cmd_parts = vec!["cc".to_string()];
cmd_parts.extend(base_flags.iter().cloned());
cmd_parts.push("-c".to_string());
cmd_parts.push(source.to_string_lossy().to_string());
cmd_parts.push("-o".to_string());
cmd_parts.push(object_file.to_string_lossy().to_string());
let cmd_str = cmd_parts.join(" ");
if verbose {
eprintln!("[compile] {}", cmd_str);
}
let output = match Command::new("cc")
.args(&cmd_parts[1..])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
{
Ok(out) => out,
Err(e) => {
let duration = start.elapsed();
return X86BuildResult {
file: source.to_path_buf(),
success: false,
duration,
object_file: None,
errors: vec![X86CompileDiagnostic {
severity: X86DiagLevel::Fatal,
message: format!("Failed to invoke compiler: {}", e),
file: Some(source.to_path_buf()),
line: None,
column: None,
code: None,
fixits: Vec::new(),
}],
warnings: Vec::new(),
exit_code: -1,
stdout: String::new(),
stderr: e.to_string(),
};
}
};
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
let duration = start.elapsed();
let success = output.status.success();
let (errors, warnings) = Self::parse_diagnostics(&stderr, source);
X86BuildResult {
file: source.to_path_buf(),
success,
duration,
object_file: if success { Some(object_file) } else { None },
errors,
warnings,
exit_code: output.status.code().unwrap_or(-1),
stdout,
stderr,
}
}
fn parse_diagnostics(
stderr: &str,
source: &Path,
) -> (Vec<X86CompileDiagnostic>, Vec<X86CompileDiagnostic>) {
let mut errors = Vec::new();
let mut warnings = Vec::new();
for line in stderr.lines() {
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
let parts: Vec<&str> = trimmed.splitn(2, ": ").collect();
let header = parts.first().unwrap_or(&"");
let message = parts.get(1).map(|s| s.to_string()).unwrap_or_default();
let (severity, file, line_num, col_num) = if header.contains("error:") {
(X86DiagLevel::Error, Some(source.to_path_buf()), None, None)
} else if header.contains("warning:") {
(
X86DiagLevel::Warning,
Some(source.to_path_buf()),
None,
None,
)
} else if header.contains("note:") {
(X86DiagLevel::Note, Some(source.to_path_buf()), None, None)
} else if header.contains("fatal error:") {
(X86DiagLevel::Fatal, Some(source.to_path_buf()), None, None)
} else {
continue;
};
let diag = X86CompileDiagnostic {
severity,
message,
file,
line: line_num,
column: col_num,
code: None,
fixits: Vec::new(),
};
match diag.severity {
X86DiagLevel::Error | X86DiagLevel::Fatal => errors.push(diag),
X86DiagLevel::Warning => warnings.push(diag),
X86DiagLevel::Note => {} }
}
(errors, warnings)
}
pub fn build_summary(&self) -> String {
let mut summary = String::new();
summary.push_str("═══════════════════════════════════════════════════════════\n");
summary.push_str(" X86 Build Summary\n");
summary.push_str("═══════════════════════════════════════════════════════════\n\n");
summary.push_str(&format!(" Total files: {}\n", self.total_files));
summary.push_str(&format!(" Succeeded: {}\n", self.succeeded));
summary.push_str(&format!(" Failed: {}\n", self.failed));
summary.push_str(&format!(" Errors: {}\n", self.errors.len()));
summary.push_str(&format!(" Warnings: {}\n", self.warnings.len()));
summary.push('\n');
if !self.errors.is_empty() {
summary.push_str("── Errors ───────────────────────────────────────────\n");
for (i, err) in self.errors.iter().enumerate() {
summary.push_str(&format!(
" {}. {}: {}\n",
i + 1,
err.file
.as_ref()
.map(|f| f.display().to_string())
.unwrap_or_default(),
err.message,
));
}
summary.push('\n');
}
summary.push_str(&self.cache.stats());
summary.push('\n');
summary.push_str("═══════════════════════════════════════════════════════════\n");
summary
}
}
pub struct X86TestRunner {
pub build_dir: PathBuf,
pub project_root: PathBuf,
pub verbose: bool,
pub results: Vec<X86TestSuiteResult>,
pub stop_on_failure: bool,
pub timeout_seconds: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct X86TestSuiteResult {
pub suite_name: String,
pub tests: Vec<X86TestResult>,
pub passed: usize,
pub failed: usize,
pub skipped: usize,
pub total_duration: Duration,
}
impl X86TestSuiteResult {
pub fn all_passed(&self) -> bool {
self.failed == 0
}
pub fn total(&self) -> usize {
self.passed + self.failed + self.skipped
}
pub fn pass_rate(&self) -> f64 {
let total = self.total();
if total == 0 {
100.0
} else {
(self.passed as f64 / total as f64) * 100.0
}
}
}
#[derive(Debug, Clone)]
pub struct X86TestResult {
pub name: String,
pub passed: bool,
pub skipped: bool,
pub duration: Duration,
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
pub error_message: Option<String>,
}
impl std::fmt::Display for X86TestResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let status = if self.passed {
"PASSED"
} else if self.skipped {
"SKIPPED"
} else {
"FAILED"
};
write!(
f,
"[{}] {} ({:.3}s)",
status,
self.name,
self.duration.as_secs_f64()
)
}
}
impl X86TestRunner {
pub fn new(build_dir: &Path, project_root: &Path, verbose: bool) -> Self {
Self {
build_dir: build_dir.to_path_buf(),
project_root: project_root.to_path_buf(),
verbose,
results: Vec::new(),
stop_on_failure: false,
timeout_seconds: None,
}
}
pub fn run_project_tests(
&mut self,
project_name: &str,
build_system: &X86BuildSystem,
) -> Vec<X86TestSuiteResult> {
let mut suites = Vec::new();
match build_system {
X86BuildSystem::CMake => {
suites.push(self.run_ctest(project_name));
}
X86BuildSystem::Make => {
suites.push(self.run_make_test(project_name));
}
X86BuildSystem::Autotools => {
suites.push(self.run_make_test(project_name));
}
X86BuildSystem::Meson => {
suites.push(self.run_meson_test(project_name));
}
_ => {
suites.push(self.run_custom_discovery(project_name));
}
}
self.results = suites.clone();
suites
}
fn run_ctest(&self, suite_name: &str) -> X86TestSuiteResult {
let mut tests = Vec::new();
let start = Instant::now();
let output = Command::new("ctest")
.arg("--output-on-failure")
.arg("--no-tests=error")
.current_dir(&self.build_dir)
.output();
match output {
Ok(out) if out.status.success() => {
let stdout = String::from_utf8_lossy(&out.stdout);
for line in stdout.lines() {
if let Some(result) = Self::parse_ctest_line(line) {
tests.push(result);
}
}
}
Ok(out) => {
tests.push(X86TestResult {
name: "ctest_suite".into(),
passed: false,
skipped: false,
duration: start.elapsed(),
stdout: String::from_utf8_lossy(&out.stdout).to_string(),
stderr: String::from_utf8_lossy(&out.stderr).to_string(),
exit_code: out.status.code().unwrap_or(-1),
error_message: Some("CTest invocation failed".into()),
});
}
Err(e) => {
tests.push(X86TestResult {
name: "ctest_not_found".into(),
passed: false,
skipped: true,
duration: Duration::ZERO,
stdout: String::new(),
stderr: e.to_string(),
exit_code: -1,
error_message: Some("CTest not available".into()),
});
}
}
let passed = tests.iter().filter(|t| t.passed).count();
let failed = tests.iter().filter(|t| !t.passed && !t.skipped).count();
let skipped = tests.iter().filter(|t| t.skipped).count();
X86TestSuiteResult {
suite_name: suite_name.to_string(),
tests,
passed,
failed,
skipped,
total_duration: start.elapsed(),
}
}
fn run_make_test(&self, suite_name: &str) -> X86TestSuiteResult {
let mut tests = Vec::new();
let start = Instant::now();
for target in &["test", "check"] {
let output = Command::new("make")
.arg(target)
.current_dir(&self.project_root)
.output();
if let Ok(out) = output {
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
if out.status.success() && (stdout.contains("PASS") || stdout.contains("OK")) {
tests.push(X86TestResult {
name: format!("make_{}", target),
passed: true,
skipped: false,
duration: start.elapsed(),
stdout: stdout.to_string(),
stderr: stderr.to_string(),
exit_code: 0,
error_message: None,
});
break;
} else if out.status.success() {
tests.push(X86TestResult {
name: format!("make_{}", target),
passed: true,
skipped: false,
duration: start.elapsed(),
stdout: stdout.to_string(),
stderr: stderr.to_string(),
exit_code: 0,
error_message: None,
});
break;
}
}
}
if tests.is_empty() {
tests.push(X86TestResult {
name: "make_test".into(),
passed: false,
skipped: true,
duration: start.elapsed(),
stdout: String::new(),
stderr: "No make test target found".into(),
exit_code: -1,
error_message: Some("make test not available".into()),
});
}
let passed = tests.iter().filter(|t| t.passed).count();
let failed = tests.iter().filter(|t| !t.passed && !t.skipped).count();
let skipped = tests.iter().filter(|t| t.skipped).count();
X86TestSuiteResult {
suite_name: suite_name.to_string(),
tests,
passed,
failed,
skipped,
total_duration: start.elapsed(),
}
}
fn run_meson_test(&self, suite_name: &str) -> X86TestSuiteResult {
let mut tests = Vec::new();
let start = Instant::now();
let output = Command::new("meson")
.args(&["test", "-C", "build"])
.current_dir(&self.project_root)
.output();
if let Ok(out) = output {
tests.push(X86TestResult {
name: "meson_test".into(),
passed: out.status.success(),
skipped: false,
duration: start.elapsed(),
stdout: String::from_utf8_lossy(&out.stdout).to_string(),
stderr: String::from_utf8_lossy(&out.stderr).to_string(),
exit_code: out.status.code().unwrap_or(-1),
error_message: if out.status.success() {
None
} else {
Some("meson test failed".into())
},
});
} else {
tests.push(X86TestResult {
name: "meson_test".into(),
passed: false,
skipped: true,
duration: Duration::ZERO,
stdout: String::new(),
stderr: "meson not available".into(),
exit_code: -1,
error_message: Some("meson not available".into()),
});
}
let passed = tests.iter().filter(|t| t.passed).count();
let failed = tests.iter().filter(|t| !t.passed && !t.skipped).count();
let skipped = tests.iter().filter(|t| t.skipped).count();
X86TestSuiteResult {
suite_name: suite_name.to_string(),
tests,
passed,
failed,
skipped,
total_duration: start.elapsed(),
}
}
fn run_custom_discovery(&self, suite_name: &str) -> X86TestSuiteResult {
let mut tests = Vec::new();
let start = Instant::now();
if let Ok(entries) = fs::read_dir(&self.build_dir) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().unwrap_or_default().to_string_lossy();
let name_lower = name.to_lowercase();
if !path.is_file() {
continue;
}
let is_test = name_lower.starts_with("test")
|| name_lower.ends_with("_test")
|| name_lower.contains("_test_")
|| name_lower == "test";
if is_test {
if let Ok(output) = Command::new(&path).output() {
let test_start = Instant::now();
let passed = output.status.success();
tests.push(X86TestResult {
name: name.to_string(),
passed,
skipped: false,
duration: test_start.elapsed(),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
exit_code: output.status.code().unwrap_or(-1),
error_message: if passed {
None
} else {
Some("Test failed".into())
},
});
}
}
}
}
if tests.is_empty() {
tests.push(X86TestResult {
name: format!("{}_no_tests", suite_name),
passed: true,
skipped: true,
duration: Duration::ZERO,
stdout: "No test executables found".into(),
stderr: String::new(),
exit_code: 0,
error_message: None,
});
}
let passed = tests.iter().filter(|t| t.passed).count();
let failed = tests.iter().filter(|t| !t.passed && !t.skipped).count();
let skipped = tests.iter().filter(|t| t.skipped).count();
X86TestSuiteResult {
suite_name: suite_name.to_string(),
tests,
passed,
failed,
skipped,
total_duration: start.elapsed(),
}
}
fn parse_ctest_line(line: &str) -> Option<X86TestResult> {
if !line.starts_with("Test #") {
return None;
}
let rest = line.trim_start_matches(|c: char| c.is_ascii_digit() || c == '#' || c == ' ');
let rest = rest.trim_start_matches(|c: char| c.is_ascii_digit());
let rest = rest.trim_start_matches(':').trim();
let parts: Vec<&str> = rest.rsplitn(2, " ").collect();
if parts.len() < 2 {
return None;
}
let status_str = parts[0].trim();
let name = parts[1].trim();
let passed = status_str.contains("Passed") || status_str.contains("passed");
let skipped = status_str.contains("Not Run");
Some(X86TestResult {
name: name.to_string(),
passed,
skipped,
duration: Duration::ZERO, stdout: String::new(),
stderr: String::new(),
exit_code: if passed { 0 } else { 1 },
error_message: if !passed && !skipped {
Some(format!("Test '{}' {}", name, status_str))
} else {
None
},
})
}
pub fn generate_junit_xml(&self) -> String {
let mut xml = String::new();
xml.push_str(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
xml.push('\n');
let total_tests: usize = self.results.iter().map(|s| s.total()).sum();
let total_failures: usize = self.results.iter().map(|s| s.failed).sum();
let total_errors: usize = 0; let total_skipped: usize = self.results.iter().map(|s| s.skipped).sum();
let total_time: f64 = self
.results
.iter()
.map(|s| s.total_duration.as_secs_f64())
.sum();
xml.push_str(&format!(
r#"<testsuites name="x86-project-tests" tests="{}" failures="{}" errors="{}" skipped="{}" time="{:.3}">"#,
total_tests, total_failures, total_errors, total_skipped, total_time,
));
xml.push('\n');
for suite in &self.results {
xml.push_str(&format!(
r#" <testsuite name="{}" tests="{}" failures="{}" errors="0" skipped="{}" time="{:.3}">"#,
suite.suite_name,
suite.total(),
suite.failed,
suite.skipped,
suite.total_duration.as_secs_f64(),
));
xml.push('\n');
for test in &suite.tests {
if test.skipped {
xml.push_str(&format!(
r#" <testcase name="{}" time="{:.3}"><skipped message="{}"/></testcase>"#,
test.name,
test.duration.as_secs_f64(),
test.error_message.as_deref().unwrap_or("skipped"),
));
} else if !test.passed {
xml.push_str(&format!(
r#" <testcase name="{}" time="{:.3}"><failure message="{}"><![CDATA[stdout: {} stderr: {}]]></failure></testcase>"#,
test.name,
test.duration.as_secs_f64(),
test.error_message.as_deref().unwrap_or("unknown"),
test.stdout,
test.stderr,
));
} else {
xml.push_str(&format!(
r#" <testcase name="{}" time="{:.3}"/>"#,
test.name,
test.duration.as_secs_f64(),
));
}
xml.push('\n');
}
xml.push_str(" </testsuite>\n");
}
xml.push_str("</testsuites>\n");
xml
}
pub fn print_summary(&self) -> String {
let mut summary = String::new();
summary.push_str("═══════════════════════════════════════════════════════════\n");
summary.push_str(" X86 Test Results Summary\n");
summary.push_str("═══════════════════════════════════════════════════════════\n\n");
for suite in &self.results {
summary.push_str(&format!(
" Suite '{}': {} tests, {} passed, {} failed, {} skipped ({:.1}%)\n",
suite.suite_name,
suite.total(),
suite.passed,
suite.failed,
suite.skipped,
suite.pass_rate(),
));
for test in &suite.tests {
let icon = if test.passed {
"✓"
} else if test.skipped {
"○"
} else {
"✗"
};
summary.push_str(&format!(
" {} {} ({:.3}s)\n",
icon,
test.name,
test.duration.as_secs_f64(),
));
}
}
let total_passed: usize = self.results.iter().map(|s| s.passed).sum();
let total_failed: usize = self.results.iter().map(|s| s.failed).sum();
let total_tests: usize = self.results.iter().map(|s| s.total()).sum();
summary.push('\n');
summary.push_str(&format!(
" Total: {} tests, {} passed, {} failed\n",
total_tests, total_passed, total_failed,
));
summary.push_str("═══════════════════════════════════════════════════════════\n");
summary
}
}
pub struct X86PackageConfig {
pub search_paths: Vec<PathBuf>,
pc_cache: HashMap<String, X86PcFileInfo>,
}
#[derive(Debug, Clone)]
pub struct X86PcFileInfo {
pub name: String,
pub version: String,
pub description: String,
pub prefix: PathBuf,
pub include_dirs: Vec<PathBuf>,
pub lib_dirs: Vec<PathBuf>,
pub libs: Vec<String>,
pub cflags: Vec<String>,
pub requires: Vec<String>,
pub requires_private: Vec<String>,
}
impl X86PackageConfig {
pub fn new() -> Self {
let mut search_paths = vec![
PathBuf::from("/usr/lib/pkgconfig"),
PathBuf::from("/usr/lib/x86_64-linux-gnu/pkgconfig"),
PathBuf::from("/usr/share/pkgconfig"),
PathBuf::from("/usr/local/lib/pkgconfig"),
PathBuf::from("/usr/local/share/pkgconfig"),
];
if let Ok(pkg_path) = std::env::var("PKG_CONFIG_PATH") {
for path in pkg_path.split(':') {
search_paths.push(PathBuf::from(path));
}
}
Self {
search_paths,
pc_cache: HashMap::new(),
}
}
pub fn add_search_path(&mut self, path: &Path) {
self.search_paths.push(path.to_path_buf());
}
pub fn parse_pc_file_for(name: &str) -> Result<X86PcFileInfo, String> {
let mut config = Self::new();
config.find_and_parse(name)
}
pub fn find_and_parse(&mut self, package: &str) -> Result<X86PcFileInfo, String> {
if let Some(info) = self.pc_cache.get(package) {
return Ok(info.clone());
}
let pc_filename = format!("{}.pc", package);
let pc_path = self
.search_paths
.iter()
.map(|dir| dir.join(&pc_filename))
.find(|p| p.exists())
.ok_or_else(|| {
format!(
"Package '{}' not found (searched for {})",
package, pc_filename
)
})?;
let info = Self::parse_pc_file(&pc_path)?;
self.pc_cache.insert(package.to_string(), info.clone());
Ok(info)
}
pub fn parse_pc_file(path: &Path) -> Result<X86PcFileInfo, String> {
let content = fs::read_to_string(path)
.map_err(|e| format!("Cannot read {}: {}", path.display(), e))?;
let mut name = String::new();
let mut version = String::new();
let mut description = String::new();
let mut prefix = PathBuf::from("/usr");
let mut include_dirs: Vec<PathBuf> = Vec::new();
let mut lib_dirs: Vec<PathBuf> = Vec::new();
let mut libs: Vec<String> = Vec::new();
let mut cflags: Vec<String> = Vec::new();
let mut requires: Vec<String> = Vec::new();
let mut requires_private: Vec<String> = Vec::new();
let variables: HashMap<String, String> = Self::parse_variables(&content);
let resolve = |s: &str, vars: &HashMap<String, String>| -> String {
let mut result = s.to_string();
for (var, val) in vars {
result = result.replace(&format!("${{{}}}", var), val);
result = result.replace(&format!("${}", var), val);
}
result
};
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
if let Some((key, value)) = Self::parse_kv(trimmed) {
let resolved = resolve(value, &variables);
match key {
"Name" => name = resolved,
"Version" => version = resolved,
"Description" => description = resolved,
"prefix" | "Prefix" => prefix = PathBuf::from(resolved),
"Cflags" | "CFlags" => {
cflags = Self::parse_flags(&resolved);
}
"Libs" => {
for part in resolved.split_whitespace() {
if part.starts_with("-L") {
lib_dirs.push(PathBuf::from(&part[2..]));
} else if part.starts_with("-l") {
libs.push(part[2..].to_string());
}
}
}
"Libs.private" => {
for part in resolved.split_whitespace() {
if part.starts_with("-l") {
libs.push(part[2..].to_string());
}
}
}
"Requires" => {
requires = resolved
.split(|c: char| c == ',' || c.is_whitespace())
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(String::from)
.collect();
}
"Requires.private" => {
requires_private = resolved
.split(|c: char| c == ',' || c.is_whitespace())
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(String::from)
.collect();
}
"includedir" | "Includedir" => {
include_dirs.push(PathBuf::from(resolved));
}
"libdir" | "Libdir" => {
lib_dirs.push(PathBuf::from(resolved));
}
_ => {}
}
}
}
if include_dirs.is_empty() {
include_dirs.push(prefix.join("include"));
}
if lib_dirs.is_empty() {
lib_dirs.push(prefix.join("lib"));
}
Ok(X86PcFileInfo {
name,
version,
description,
prefix,
include_dirs,
lib_dirs,
libs,
cflags,
requires,
requires_private,
})
}
fn parse_variables(content: &str) -> HashMap<String, String> {
let mut vars = HashMap::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
if let Some(eq) = trimmed.find('=') {
let key = trimmed[..eq].trim();
let value = trimmed[eq + 1..].trim();
if !key.contains(':') && !key.contains(" ") && !key.is_empty() {
vars.insert(key.to_string(), value.to_string());
}
}
}
vars
}
fn parse_kv<'a>(line: &'a str) -> Option<(&'a str, &'a str)> {
let colon_pos = line.find(':')?;
let key = line[..colon_pos].trim();
let value = line[colon_pos + 1..].trim();
Some((key, value))
}
fn parse_flags(flags_str: &str) -> Vec<String> {
let mut flags = Vec::new();
let mut current = String::new();
let mut in_quotes = false;
for c in flags_str.chars() {
match c {
'"' => in_quotes = !in_quotes,
' ' if !in_quotes => {
if !current.is_empty() {
flags.push(std::mem::take(&mut current));
}
}
_ => current.push(c),
}
}
if !current.is_empty() {
flags.push(current);
}
flags
}
pub fn check_version(actual: &str, required: &str) -> Result<bool, String> {
let req_op = if required.starts_with(">=") {
(">=", &required[2..])
} else if required.starts_with("<=") {
("<=", &required[2..])
} else if required.starts_with('>') {
(">", &required[1..])
} else if required.starts_with('<') {
("<", &required[1..])
} else if required.starts_with("==") || required.starts_with('=') {
let v = required.trim_start_matches('=');
("==", v)
} else {
("==", required)
};
let req_ver = req_op.1.trim();
let act_parts: Vec<u32> = actual.split('.').filter_map(|s| s.parse().ok()).collect();
let req_parts: Vec<u32> = req_ver.split('.').filter_map(|s| s.parse().ok()).collect();
let cmp = act_parts.cmp(&req_parts);
match req_op.0 {
">=" => Ok(cmp != std::cmp::Ordering::Less),
"<=" => Ok(cmp != std::cmp::Ordering::Greater),
">" => Ok(cmp == std::cmp::Ordering::Greater),
"<" => Ok(cmp == std::cmp::Ordering::Less),
"==" | "=" => Ok(cmp == std::cmp::Ordering::Equal),
_ => Err(format!("Unknown version operator: {}", req_op.0)),
}
}
pub fn resolve_requires(&mut self, package: &str) -> Result<Vec<X86PcFileInfo>, String> {
let mut resolved = Vec::new();
let mut visited = HashSet::new();
self.resolve_requires_recursive(package, &mut resolved, &mut visited)?;
Ok(resolved)
}
fn resolve_requires_recursive(
&mut self,
package: &str,
resolved: &mut Vec<X86PcFileInfo>,
visited: &mut HashSet<String>,
) -> Result<(), String> {
if visited.contains(package) {
return Ok(()); }
visited.insert(package.to_string());
let info = self.find_and_parse(package)?;
for req in info.requires.clone() {
self.resolve_requires_recursive(&req, resolved, visited)?;
}
for req in info.requires_private.clone() {
self.resolve_requires_recursive(&req, resolved, visited)?;
}
resolved.push(info);
Ok(())
}
pub fn combined_cflags(deps: &[X86PcFileInfo]) -> Vec<String> {
let mut flags = Vec::new();
for dep in deps {
flags.extend(dep.cflags.clone());
for dir in &dep.include_dirs {
if !flags.contains(&format!("-I{}", dir.display())) {
flags.push(format!("-I{}", dir.display()));
}
}
}
flags.sort();
flags.dedup();
flags
}
pub fn combined_libs(deps: &[X86PcFileInfo]) -> Vec<String> {
let mut flags = Vec::new();
for dep in deps {
for dir in &dep.lib_dirs {
let flag = format!("-L{}", dir.display());
if !flags.contains(&flag) {
flags.push(flag);
}
}
for lib in &dep.libs {
let flag = format!("-l{}", lib);
if !flags.contains(&flag) {
flags.push(flag);
}
}
}
flags
}
}
impl Default for X86PackageConfig {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::io::Write;
fn create_temp_dir() -> PathBuf {
let mut dir = env::temp_dir();
dir.push(format!("x86_proj_test_{}", rand::random::<u32>()));
fs::create_dir_all(&dir).unwrap();
dir
}
fn cleanup_temp_dir(dir: &Path) {
let _ = fs::remove_dir_all(dir);
}
fn create_file(path: &Path, content: &str) {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).unwrap();
}
fs::write(path, content).unwrap();
}
#[test]
fn test_build_system_as_str() {
assert_eq!(X86BuildSystem::CMake.as_str(), "cmake");
assert_eq!(X86BuildSystem::Make.as_str(), "make");
assert_eq!(X86BuildSystem::Autotools.as_str(), "autotools");
assert_eq!(X86BuildSystem::Meson.as_str(), "meson");
assert_eq!(X86BuildSystem::Bazel.as_str(), "bazel");
assert_eq!(X86BuildSystem::Cargo.as_str(), "cargo");
assert_eq!(X86BuildSystem::Unknown.as_str(), "unknown");
}
#[test]
fn test_build_system_default_build_file() {
assert_eq!(
X86BuildSystem::CMake.default_build_file(),
vec!["CMakeLists.txt"]
);
assert!(X86BuildSystem::Make
.default_build_file()
.contains(&"Makefile"));
assert!(X86BuildSystem::Bazel
.default_build_file()
.contains(&"BUILD"));
assert!(X86BuildSystem::Cargo
.default_build_file()
.contains(&"Cargo.toml"));
}
#[test]
fn test_detect_cmake_project() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(test)");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::CMake);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_make_project() {
let dir = create_temp_dir();
create_file(&dir.join("Makefile"), "all:\n\tcc -o test test.c");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Make);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_autotools_project() {
let dir = create_temp_dir();
create_file(&dir.join("configure.ac"), "AC_INIT(test, 1.0)");
create_file(&dir.join("Makefile.am"), "");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Autotools);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_meson_project() {
let dir = create_temp_dir();
create_file(&dir.join("meson.build"), "project('test')");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Meson);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_bazel_project() {
let dir = create_temp_dir();
create_file(&dir.join("BUILD.bazel"), "cc_binary(name='test')");
create_file(&dir.join("WORKSPACE"), "workspace(name='test')");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Bazel);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_cargo_project() {
let dir = create_temp_dir();
create_file(&dir.join("Cargo.toml"), "[package]\nname = \"test\"");
create_file(&dir.join("build.rs"), "fn main() {}");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Cargo);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_unknown_project() {
let dir = create_temp_dir();
create_file(&dir.join("hello.c"), "int main() { return 0; }");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Unknown);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_with_source_files() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(test)");
create_file(&dir.join("main.c"), "int main() { return 0; }");
create_file(&dir.join("src/util.c"), "int util() { return 1; }");
create_file(&dir.join("include/util.h"), "#pragma once");
let detector = X86ProjectDetector::scan(&dir);
let sources = detector.collect_sources();
assert!(!sources.is_empty(), "Should find source files");
cleanup_temp_dir(&dir);
}
#[test]
fn test_parse_cmake_variables() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"cmake_minimum_required(VERSION 3.10)\nproject(MyProject VERSION 1.2.3)\nset(MY_VAR hello)\noption(ENABLE_FOO \"Enable foo\" ON)",
);
let vars = X86ProjectDetector::parse_build_variables(&dir, X86BuildSystem::CMake);
assert!(vars.contains_key("PROJECT_NAME"));
assert!(vars.contains_key("PROJECT_VERSION"));
assert_eq!(vars.get("MY_VAR").map(|s| s.as_str()), Some("hello"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_parse_makefile_variables() {
let dir = create_temp_dir();
create_file(
&dir.join("Makefile"),
"CC = gcc\nCFLAGS = -O2 -Wall\nLIBS = -lm",
);
let vars = X86ProjectDetector::parse_build_variables(&dir, X86BuildSystem::Make);
assert_eq!(vars.get("CC").map(|s| s.as_str()), Some("gcc"));
assert_eq!(vars.get("LIBS").map(|s| s.as_str()), Some("-lm"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_discover_cmake_targets() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"add_executable(myapp main.c)\nadd_library(mylib STATIC lib.c)",
);
let targets =
X86ProjectDetector::discover_targets(&dir, X86BuildSystem::CMake, &HashMap::new());
assert!(targets.iter().any(|t| t.name == "myapp"));
assert!(targets.iter().any(|t| t.name == "mylib"));
assert_eq!(
targets.iter().find(|t| t.name == "mylib").unwrap().kind,
X86TargetKind::StaticLibrary,
);
cleanup_temp_dir(&dir);
}
#[test]
fn test_collect_include_dirs() {
let dir = create_temp_dir();
create_file(&dir.join("include").join("test.h"), "");
create_file(&dir.join("src").join("header.h"), "");
let detector = X86ProjectDetector::scan(&dir);
let includes = detector.collect_include_dirs();
assert!(includes.iter().any(|d| d == &dir));
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_version_from_cmake() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(Test VERSION 2.5.0)");
let vars = X86ProjectDetector::parse_build_variables(&dir, X86BuildSystem::CMake);
let version = X86ProjectDetector::detect_version(&dir, X86BuildSystem::CMake, &vars);
assert_eq!(version, Some("2.5.0".to_string()));
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_platform_linux() {
assert_eq!(
X86ConfigureDetector::detect_platform("x86_64-unknown-linux-gnu"),
X86Platform::Linux,
);
}
#[test]
fn test_detect_platform_windows() {
assert_eq!(
X86ConfigureDetector::detect_platform("x86_64-pc-windows-msvc"),
X86Platform::Windows,
);
}
#[test]
fn test_detect_platform_darwin() {
assert_eq!(
X86ConfigureDetector::detect_platform("x86_64-apple-darwin"),
X86Platform::Darwin,
);
}
#[test]
fn test_detect_architecture_x86_64() {
assert_eq!(
X86ConfigureDetector::detect_architecture("x86_64-unknown-linux-gnu"),
X86ArchVariant::X86_64,
);
}
#[test]
fn test_detect_architecture_x86_32() {
assert_eq!(
X86ConfigureDetector::detect_architecture("i686-unknown-linux-gnu"),
X86ArchVariant::X86_32,
);
}
#[test]
fn test_arch_variant_is_64_bit() {
assert!(X86ArchVariant::X86_64.is_64_bit());
assert!(!X86ArchVariant::X86_32.is_64_bit());
}
#[test]
fn test_arch_variant_pointer_width() {
assert_eq!(X86ArchVariant::X86_64.pointer_width(), 64);
assert_eq!(X86ArchVariant::X86_32.pointer_width(), 32);
}
#[test]
fn test_arch_variant_stack_alignment() {
assert_eq!(
X86ArchVariant::X86_64.stack_alignment(),
X86_STACK_ALIGNMENT_64
);
assert_eq!(
X86ArchVariant::X86_32.stack_alignment(),
X86_STACK_ALIGNMENT_32
);
}
#[test]
fn test_simd_level_ordering() {
assert!(X86SIMDLevel::AVX2 > X86SIMDLevel::SSE2);
assert!(X86SIMDLevel::SSE2 > X86SIMDLevel::None);
assert!(X86SIMDLevel::AVX512F > X86SIMDLevel::AVX2);
}
#[test]
fn test_simd_level_compile_flags() {
assert!(X86SIMDLevel::SSE2.compile_flags().contains(&"-msse2"));
assert!(X86SIMDLevel::AVX2.compile_flags().contains(&"-mavx2"));
assert!(X86SIMDLevel::None.compile_flags().is_empty());
}
#[test]
fn test_x86_features_highest_simd() {
let mut features = X86Features::default();
assert_eq!(features.highest_simd(), X86SIMDLevel::None);
features.sse2 = true;
features.sse = true;
features.mmx = true;
assert_eq!(features.highest_simd(), X86SIMDLevel::SSE2);
features.avx2 = true;
features.avx = true;
assert_eq!(features.highest_simd(), X86SIMDLevel::AVX2);
}
#[test]
fn test_platform_host() {
let host = X86Platform::host();
assert!(matches!(
host,
X86Platform::Linux
| X86Platform::Windows
| X86Platform::Darwin
| X86Platform::FreeBSD
| X86Platform::Unix
| X86Platform::Unknown
));
}
#[test]
fn test_detect_defines_for_x86_64_linux() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(test)");
let detector = X86ProjectDetector::scan(&dir);
let features = X86Features::default();
let defines = X86ConfigureDetector::detect_defines(
&dir,
&detector,
X86Platform::Linux,
X86ArchVariant::X86_64,
&features,
);
assert!(defines.iter().any(|(k, _)| k == "__linux__"));
assert!(defines.iter().any(|(k, _)| k == "__x86_64__"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_defines_with_features() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(test)");
let detector = X86ProjectDetector::scan(&dir);
let mut features = X86Features::default();
features.sse2 = true;
features.avx = true;
let defines = X86ConfigureDetector::detect_defines(
&dir,
&detector,
X86Platform::Linux,
X86ArchVariant::X86_64,
&features,
);
assert!(defines.iter().any(|(k, _)| k == "__SSE2__"));
assert!(defines.iter().any(|(k, _)| k == "__AVX__"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_package_manager_detect() {
let manager = X86PackageManager::detect();
let _ = manager.as_str();
}
#[test]
fn test_package_manager_install_command() {
let cmd = X86PackageManager::Apt.install_command("zlib1g-dev");
assert!(cmd.is_some());
assert!(cmd.unwrap().contains(&"zlib1g-dev".to_string()));
}
#[test]
fn test_detect_cmake_deps() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"find_package(ZLIB REQUIRED)\nfind_package(OpenSSL 3.0)",
);
let detector = X86ProjectDetector::scan(&dir);
let deps = X86DependencyResolver::detect_cmake_deps(&dir, &detector);
assert!(deps.contains_key("ZLIB"));
assert!(deps.contains_key("OpenSSL"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_header_libs_uthash() {
let dir = create_temp_dir();
create_file(&dir.join("uthash.h"), "/* uthash header */");
let libs = X86DependencyResolver::detect_header_libs(&dir);
assert!(libs.iter().any(|l| l.name == "uthash"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_header_libs_stb() {
let dir = create_temp_dir();
create_file(&dir.join("include").join("stb_image.h"), "/* stb */");
let libs = X86DependencyResolver::detect_header_libs(&dir);
assert!(libs.iter().any(|l| l.name == "stb"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_generate_dependency_report() {
let map = X86DependencyMap::default();
let report = X86DependencyResolver::generate_report(&map);
assert!(report.contains("Dependencies resolved"));
assert!(report.contains("pkg-config modules"));
}
#[test]
fn test_compiler_creation() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::new(&dir);
assert_eq!(compiler.target_triple, X86_64_LINUX_TRIPLE);
assert!(compiler.project_config.is_none());
assert!(compiler.build_results.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_x86_64_linux() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::x86_64_linux(&dir);
assert_eq!(compiler.target_triple, X86_64_LINUX_TRIPLE);
assert_eq!(compiler.target_cpu, "x86-64");
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_x86_32_linux() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::x86_32_linux(&dir);
assert_eq!(compiler.target_triple, X86_32_LINUX_TRIPLE);
assert_eq!(compiler.target_cpu, "pentium4");
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_with_opt_level() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::new(&dir)
.with_opt_level(X86OptLevel::O3)
.with_verbose(true)
.with_parallel_jobs(4);
assert!(compiler.verbose);
assert_eq!(compiler.parallel_jobs, 4);
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_detect_project() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"project(Test)\nadd_executable(test main.c)",
);
create_file(&dir.join("main.c"), "int main() { return 0; }");
let mut compiler = X86RealProjectCompiler::new(&dir);
assert!(compiler.detect_project().is_ok());
assert!(compiler.project_config.is_some());
let config = compiler.project_config.unwrap();
assert_eq!(config.build_system, X86BuildSystem::CMake);
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_generate_report() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(Test)");
let mut compiler = X86RealProjectCompiler::new(&dir);
compiler.detect_project().ok();
let report = compiler.generate_report();
assert!(report.contains("Test"));
assert!(report.contains("cmake"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_error_and_warning_count() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::new(&dir);
assert_eq!(compiler.error_count(), 0);
assert_eq!(compiler.warning_count(), 0);
assert!(!compiler.build_succeeded());
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_cache_new() {
let dir = create_temp_dir();
let cache = X86BuildCache::new(&dir.join(".cache"));
assert!(cache.enabled);
assert_eq!(cache.hits, 0);
assert_eq!(cache.misses, 0);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_cache_disable_enable() {
let dir = create_temp_dir();
let mut cache = X86BuildCache::new(&dir.join(".cache"));
cache.disable();
assert!(!cache.enabled);
cache.enable();
assert!(cache.enabled);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_cache_hit_rate() {
let dir = create_temp_dir();
let mut cache = X86BuildCache::new(&dir.join(".cache"));
assert_eq!(cache.hit_rate(), 0.0);
cache.hits = 3;
cache.misses = 1;
assert_eq!(cache.hit_rate(), 0.75);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_cache_clear() {
let dir = create_temp_dir();
let mut cache = X86BuildCache::new(&dir.join(".cache"));
cache.hits = 10;
cache.misses = 5;
cache.current_size = 1024;
cache.clear();
assert_eq!(cache.hits, 0);
assert_eq!(cache.current_size, 0);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_runner_new() {
let dir = create_temp_dir();
let runner = X86BuildRunner::new(&dir, 2, true);
assert_eq!(runner.parallel_jobs, 2);
assert!(runner.verbose);
assert_eq!(runner.total_files, 0);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_summary() {
let dir = create_temp_dir();
let mut runner = X86BuildRunner::new(&dir, 1, false);
runner.total_files = 5;
runner.succeeded = 4;
runner.failed = 1;
runner.errors.push(X86CompileDiagnostic {
severity: X86DiagLevel::Error,
message: "test error".into(),
file: Some(dir.join("test.c")),
line: None,
column: None,
code: None,
fixits: Vec::new(),
});
let summary = runner.build_summary();
assert!(summary.contains("Total files: 5"));
assert!(summary.contains("Succeeded: 4"));
assert!(summary.contains("Failed: 1"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_parse_diagnostics() {
let dir = create_temp_dir();
let stderr = "test.c:10:5: error: expected ';' after expression\n";
let (errors, warnings) = X86BuildRunner::parse_diagnostics(stderr, &dir.join("test.c"));
assert!(!errors.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_runner_new() {
let dir = create_temp_dir();
let runner = X86TestRunner::new(&dir, &dir, false);
assert!(!runner.verbose);
assert!(runner.results.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_test_suite_result_all_passed() {
let mut suite = X86TestSuiteResult {
suite_name: "test".into(),
tests: Vec::new(),
passed: 10,
failed: 0,
skipped: 0,
total_duration: Duration::from_secs(1),
};
assert!(suite.all_passed());
suite.failed = 1;
assert!(!suite.all_passed());
}
#[test]
fn test_test_suite_result_total() {
let suite = X86TestSuiteResult {
suite_name: "test".into(),
tests: Vec::new(),
passed: 8,
failed: 2,
skipped: 3,
total_duration: Duration::ZERO,
};
assert_eq!(suite.total(), 13);
}
#[test]
fn test_test_suite_result_pass_rate() {
let suite = X86TestSuiteResult {
suite_name: "test".into(),
tests: Vec::new(),
passed: 8,
failed: 2,
skipped: 0,
total_duration: Duration::ZERO,
};
assert!((suite.pass_rate() - 80.0).abs() < 0.01);
}
#[test]
fn test_test_result_display() {
let result = X86TestResult {
name: "my_test".into(),
passed: true,
skipped: false,
duration: Duration::from_millis(123),
stdout: String::new(),
stderr: String::new(),
exit_code: 0,
error_message: None,
};
let display = format!("{}", result);
assert!(display.contains("PASSED"));
assert!(display.contains("my_test"));
}
#[test]
fn test_generate_junit_xml() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
runner.results.push(X86TestSuiteResult {
suite_name: "suite1".into(),
tests: vec![
X86TestResult {
name: "test1".into(),
passed: true,
skipped: false,
duration: Duration::from_millis(10),
stdout: String::new(),
stderr: String::new(),
exit_code: 0,
error_message: None,
},
X86TestResult {
name: "test2".into(),
passed: false,
skipped: false,
duration: Duration::from_millis(5),
stdout: String::new(),
stderr: "boom".into(),
exit_code: 1,
error_message: Some("failed".into()),
},
],
passed: 1,
failed: 1,
skipped: 0,
total_duration: Duration::from_millis(15),
});
let xml = runner.generate_junit_xml();
assert!(xml.contains("<testsuites"));
assert!(xml.contains("<testsuite name=\"suite1\""));
assert!(xml.contains("test1"));
assert!(xml.contains("test2"));
assert!(xml.contains("<failure"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_print_summary() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
runner.results.push(X86TestSuiteResult {
suite_name: "suite1".into(),
tests: vec![X86TestResult {
name: "t1".into(),
passed: true,
skipped: false,
duration: Duration::ZERO,
stdout: String::new(),
stderr: String::new(),
exit_code: 0,
error_message: None,
}],
passed: 1,
failed: 0,
skipped: 0,
total_duration: Duration::ZERO,
});
let summary = runner.print_summary();
assert!(summary.contains("suite1"));
assert!(summary.contains("t1"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_package_config_new() {
let config = X86PackageConfig::new();
assert!(!config.search_paths.is_empty());
assert!(config
.search_paths
.iter()
.any(|p| p.to_string_lossy().contains("pkgconfig")));
}
#[test]
fn test_package_config_add_search_path() {
let mut config = X86PackageConfig::new();
let new_path = PathBuf::from("/custom/pkgconfig");
config.add_search_path(&new_path);
assert!(config.search_paths.contains(&new_path));
}
#[test]
fn test_parse_pc_file() {
let dir = create_temp_dir();
let pc_path = dir.join("zlib.pc");
create_file(
&pc_path,
"prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: zlib
Version: 1.3
Description: zlib compression library
Cflags: -I${includedir}
Libs: -L${libdir} -lz
Requires: ",
);
let info = X86PackageConfig::parse_pc_file(&pc_path).unwrap();
assert_eq!(info.name, "zlib");
assert_eq!(info.version, "1.3");
assert!(info.libs.contains(&"z".to_string()));
cleanup_temp_dir(&dir);
}
#[test]
fn test_parse_pc_file_with_requires() {
let dir = create_temp_dir();
let pc_path = dir.join("libpng.pc");
create_file(
&pc_path,
"prefix=/usr
Name: libpng
Version: 1.6.40
Description: PNG library
Requires: zlib
Libs: -L${prefix}/lib -lpng
Cflags: -I${prefix}/include",
);
let info = X86PackageConfig::parse_pc_file(&pc_path).unwrap();
assert_eq!(info.name, "libpng");
assert!(info.requires.contains(&"zlib".to_string()));
cleanup_temp_dir(&dir);
}
#[test]
fn test_check_version_equal() {
assert!(X86PackageConfig::check_version("1.2.3", "1.2.3").unwrap());
assert!(!X86PackageConfig::check_version("1.2.3", "1.2.4").unwrap());
}
#[test]
fn test_check_version_greater_equal() {
assert!(X86PackageConfig::check_version("1.5", ">= 1.0").unwrap());
assert!(X86PackageConfig::check_version("1.0", ">= 1.0").unwrap());
assert!(!X86PackageConfig::check_version("0.9", ">= 1.0").unwrap());
}
#[test]
fn test_check_version_greater() {
assert!(X86PackageConfig::check_version("2.0", "> 1.5").unwrap());
assert!(!X86PackageConfig::check_version("1.0", "> 1.0").unwrap());
}
#[test]
fn test_check_version_less() {
assert!(X86PackageConfig::check_version("1.0", "< 2.0").unwrap());
assert!(!X86PackageConfig::check_version("2.0", "< 2.0").unwrap());
}
#[test]
fn test_parse_flags() {
let flags = X86PackageConfig::parse_flags("-I/usr/include -DDEBUG -O2");
assert_eq!(flags.len(), 3);
assert!(flags.contains(&"-I/usr/include".to_string()));
assert!(flags.contains(&"-DDEBUG".to_string()));
assert!(flags.contains(&"-O2".to_string()));
}
#[test]
fn test_combined_cflags() {
let mut deps = Vec::new();
deps.push(X86PcFileInfo {
name: "a".into(),
version: "1".into(),
description: String::new(),
prefix: PathBuf::from("/usr"),
include_dirs: vec![PathBuf::from("/usr/include/a")],
lib_dirs: Vec::new(),
libs: Vec::new(),
cflags: vec!["-DA".into()],
requires: Vec::new(),
requires_private: Vec::new(),
});
let cflags = X86PackageConfig::combined_cflags(&deps);
assert!(cflags.contains(&"-I/usr/include/a".to_string()));
assert!(cflags.contains(&"-DA".to_string()));
}
#[test]
fn test_sqlite_config_default() {
let config = X86SQLiteConfig::default();
assert!(config.amalgamation);
assert_eq!(config.threading_mode, 2);
assert!(config.enable_fts5);
assert!(config.enable_rtree);
}
#[test]
fn test_sqlite_defines() {
let config = X86SQLiteConfig::default();
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "SQLITE_THREADSAFE"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_FTS5"));
}
#[test]
fn test_sqlite_compiler_flags() {
let config = X86SQLiteConfig::default();
let flags = config.compiler_flags();
assert!(flags.contains(&"-std=c99".to_string()));
}
#[test]
fn test_zlib_config_default() {
let config = X86ZlibConfig::default();
assert!(config.asm_optimizations);
assert!(!config.gz_trace);
}
#[test]
fn test_zlib_sources() {
let dir = create_temp_dir();
create_file(&dir.join("adler32.c"), "");
create_file(&dir.join("deflate.c"), "");
create_file(&dir.join("zutil.c"), "");
let config = X86ZlibConfig::default();
let sources = config.sources(&dir);
assert!(!sources.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_libpng_config_default() {
let config = X86LibpngConfig::default();
assert!(config.has_zlib);
assert_eq!(config.simd_level, X86SIMDLevel::SSE2);
}
#[test]
fn test_libpng_defines_with_simd() {
let config = X86LibpngConfig {
simd_level: X86SIMDLevel::AVX2,
..Default::default()
};
let defs = config.defines();
let sse_def = defs.iter().find(|(k, _)| k == "PNG_INTEL_SSE");
assert!(sse_def.is_some());
assert_eq!(sse_def.unwrap().1, Some("1".to_string()));
}
#[test]
fn test_jpeg_turbo_config_default() {
let config = X86JpegTurboConfig::default();
assert!(config.with_turbojpeg);
assert_eq!(config.simd_level, X86SIMDLevel::SSE2);
}
#[test]
fn test_jpeg_turbo_simd_sources() {
let dir = create_temp_dir();
let simd_dir = dir.join("simd");
fs::create_dir_all(&simd_dir).unwrap();
create_file(&dir.join("jcapimin.c"), "");
create_file(&simd_dir.join("jsimd_x86_64.c"), "");
let config = X86JpegTurboConfig::default();
let sources = config.sources(&dir);
assert!(!sources.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_openssl_config_emulate_configure() {
let config = X86OpenSSLConfig::default();
let args = config.emulate_configure();
assert!(args.contains(&"./Configure".to_string()));
assert!(args.contains(&"linux-x86_64".to_string()));
assert!(args.contains(&"shared".to_string()));
}
#[test]
fn test_curl_config_default() {
let config = X86CurlConfig::default();
assert!(config.enable_http);
assert!(config.enable_ssl);
assert!(config.with_zlib);
assert!(!config.enable_http2);
}
#[test]
fn test_curl_tls_backend() {
let config = X86CurlConfig {
tls_backend: X86TlsBackend::OpenSSL,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "USE_SSL"));
}
#[test]
fn test_nginx_config_default() {
let config = X86NginxConfig::default();
assert!(config.http);
assert!(!config.mail);
assert!(!config.stream);
assert_eq!(config.modules.len(), 3);
assert!(config.modules.iter().all(|m| m.enabled));
}
#[test]
fn test_redis_config_default() {
let config = X86RedisConfig::default();
assert!(config.use_jemalloc);
assert_eq!(config.opt_flags, "-O2");
}
#[test]
fn test_lua_config_default() {
let config = X86LuaConfig::default();
assert_eq!(config.lua_version, "5.4");
assert!(config.build_interpreter);
assert!(config.with_readline);
}
#[test]
fn test_lua_config_float() {
let config = X86LuaConfig {
float_width: X86LuaFloat::LongDouble,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "LUA_FLOAT_LONGDOUBLE"));
}
#[test]
fn test_jsonc_config_default() {
let config = X86JsonCConfig::default();
assert!(config.enable_threading);
assert!(config.enable_rdrand);
}
#[test]
fn test_uthash_config() {
let config = X86UthashConfig;
assert_eq!(config.name(), "uthash");
let sources = config.sources(&PathBuf::from("/tmp"));
assert!(sources.is_empty()); assert!(config.notes().iter().any(|n| n.contains("header-only")));
}
#[test]
fn test_stb_config_default() {
let config = X86StbConfig::default();
assert_eq!(config.libraries.len(), 2);
}
#[test]
fn test_stb_library_headers() {
assert_eq!(X86StbLibrary::Image.header_name(), "stb_image.h",);
assert!(!X86StbLibrary::Image.implementation_defines().is_empty());
}
#[test]
fn test_doctest_config() {
let config = X86DoctestConfig::default();
assert_eq!(config.cxx_standard, "c++17");
assert!(config.build_tests);
}
#[test]
fn test_fmtlib_config_default() {
let config = X86FmtlibConfig::default();
assert!(!config.header_only);
assert!(config.enable_compile_checks);
}
#[test]
fn test_fmtlib_config_header_only() {
let config = X86FmtlibConfig {
header_only: true,
..Default::default()
};
let sources = config.sources(&PathBuf::from("/tmp"));
assert!(sources.is_empty());
let defs = config.defines();
assert!(defs
.iter()
.any(|(k, v)| k == "FMT_HEADER_ONLY" && v == &Some("1".to_string())));
}
#[test]
fn test_spdlog_config_default() {
let config = X86SpdlogConfig::default();
assert!(config.header_only);
assert!(!config.compile_library);
}
#[test]
fn test_spdlog_config_compiled() {
let config = X86SpdlogConfig {
header_only: false,
compile_library: true,
..Default::default()
};
let defs = config.defines();
assert!(defs
.iter()
.any(|(k, v)| k == "SPDLOG_COMPILED_LIB" && v == &Some("1".to_string())));
}
#[test]
fn test_compilation_database_sqlite() {
let dir = create_temp_dir();
create_file(&dir.join("sqlite3.c"), "");
let config = X86SQLiteConfig::default();
let db = config.compile_database(&dir, &dir.join("build"));
assert!(!db.entries.is_empty());
assert_eq!(db.project_name, "sqlite");
cleanup_temp_dir(&dir);
}
#[test]
fn test_compilation_database_zlib() {
let dir = create_temp_dir();
for f in &["adler32.c", "deflate.c", "zutil.c"] {
create_file(&dir.join(f), "");
}
let config = X86ZlibConfig::default();
let db = config.compile_database(&dir, &dir.join("build"));
assert!(!db.entries.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_full_detect_and_config() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"project(Test VERSION 1.0)\nadd_executable(app main.c)",
);
create_file(&dir.join("main.c"), "int main(void) { return 0; }");
let mut compiler = X86RealProjectCompiler::x86_64_linux(&dir);
let config = compiler.detect_project().unwrap();
assert_eq!(config.name.as_str(), dir.file_name().unwrap());
assert_eq!(config.build_system, X86BuildSystem::CMake);
assert!(!config.sources.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_diag_level_ordering() {
assert!(X86DiagLevel::Error > X86DiagLevel::Warning);
assert!(X86DiagLevel::Fatal > X86DiagLevel::Error);
assert!(X86DiagLevel::Warning > X86DiagLevel::Note);
}
#[test]
fn test_diag_level_display() {
assert_eq!(format!("{}", X86DiagLevel::Error), "error");
assert_eq!(format!("{}", X86DiagLevel::Warning), "warning");
assert_eq!(format!("{}", X86DiagLevel::Note), "note");
assert_eq!(format!("{}", X86DiagLevel::Fatal), "fatal error");
}
#[test]
fn test_target_kind_display() {
assert_eq!(X86TargetKind::Executable.as_str(), "executable");
assert_eq!(X86TargetKind::StaticLibrary.as_str(), "static_library");
assert_eq!(X86TargetKind::HeaderOnly.as_str(), "header_only");
}
#[test]
fn test_project_outcome() {
let outcome = X86ProjectOutcome {
project_name: "test".into(),
build_success: true,
test_success: true,
build_results: Vec::new(),
test_results: Vec::new(),
total_duration: Duration::from_secs(10),
};
assert!(outcome.build_success);
assert!(outcome.test_success);
}
#[test]
fn test_x86_constants_consistency() {
assert_eq!(X86_64_LINUX_TRIPLE, "x86_64-unknown-linux-gnu");
assert_eq!(X86_32_LINUX_TRIPLE, "i686-unknown-linux-gnu");
assert!(!X86_CPU_FEATURES.is_empty());
assert!(X86_CPU_FEATURES.contains(&"sse2"));
assert!(X86_MICROARCHS.contains(&"x86-64-v3"));
}
#[test]
fn test_extract_parens_simple() {
let result = X86ProjectDetector::extract_parens("project(test)", "project");
assert_eq!(result, Some("test".to_string()));
}
#[test]
fn test_extract_parens_nested() {
let result =
X86ProjectDetector::extract_parens("find_package(ZLIB 1.2 REQUIRED)", "find_package");
assert_eq!(result, Some("ZLIB 1.2 REQUIRED".to_string()));
}
#[test]
fn test_extract_parens_not_found() {
let result = X86ProjectDetector::extract_parens("noparens", "noparens");
assert!(result.is_none());
}
#[test]
fn test_x86_config_windows_triple() {
assert_eq!(X86_64_WINDOWS_TRIPLE, "x86_64-pc-windows-msvc");
let arch = X86ConfigureDetector::detect_architecture(X86_64_WINDOWS_TRIPLE);
assert_eq!(arch, X86ArchVariant::X86_64);
let platform = X86ConfigureDetector::detect_platform(X86_64_WINDOWS_TRIPLE);
assert_eq!(platform, X86Platform::Windows);
}
#[test]
fn test_x86_config_darwin_triple() {
assert_eq!(X86_64_DARWIN_TRIPLE, "x86_64-apple-darwin");
let platform = X86ConfigureDetector::detect_platform(X86_64_DARWIN_TRIPLE);
assert_eq!(platform, X86Platform::Darwin);
}
#[test]
fn test_simd_level_for_x86_64_default() {
let mut features = X86Features::default();
features.sse2 = true;
features.sse = true;
assert_eq!(features.highest_simd(), X86SIMDLevel::SSE2);
}
#[test]
fn test_build_cache_stats_format() {
let dir = create_temp_dir();
let mut cache = X86BuildCache::new(&dir.join(".cache"));
cache.hits = 75;
cache.misses = 25;
let stats = cache.stats();
assert!(stats.contains("75.0"));
assert!(stats.contains("hit rate"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_multiple_project_configs_roundtrip() {
let configs: Vec<Box<dyn X86ProjectConfigurable>> = vec![
Box::new(X86SQLiteConfig::default()),
Box::new(X86ZlibConfig::default()),
Box::new(X86LibpngConfig::default()),
Box::new(X86JpegTurboConfig::default()),
Box::new(X86OpenSSLConfig::default()),
Box::new(X86CurlConfig::default()),
Box::new(X86NginxConfig::default()),
Box::new(X86RedisConfig::default()),
Box::new(X86LuaConfig::default()),
Box::new(X86JsonCConfig::default()),
Box::new(X86UthashConfig),
Box::new(X86StbConfig::default()),
Box::new(X86DoctestConfig::default()),
Box::new(X86FmtlibConfig::default()),
Box::new(X86SpdlogConfig::default()),
];
for config in &configs {
let name = config.name();
assert!(!name.is_empty(), "Config should have a name");
let defs = config.defines();
let flags = config.compiler_flags();
let bs = config.build_system();
let _ = defs;
let _ = flags;
let _ = bs;
}
}
#[test]
fn test_build_runner_auto_parallel_jobs() {
let dir = create_temp_dir();
let runner = X86BuildRunner::new(&dir, 0, false);
assert!(runner.parallel_jobs >= 1);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_runner_compile_empty_sources() {
let dir = create_temp_dir();
let mut runner = X86BuildRunner::new(&dir, 2, false);
let result = runner.compile_project(&[], &[], &[], &[], &X86CompileOptions::default());
assert!(result.is_ok());
assert!(result.unwrap().is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_runner_cache_disabled_lookup() {
let dir = create_temp_dir();
let mut cache = X86BuildCache::new(&dir.join(".cache"));
cache.disable();
let source = dir.join("test.c");
create_file(&source, "int main(){return 0;}");
let result = cache.lookup(&source, &["-O2".to_string()]);
assert!(result.is_none());
assert_eq!(cache.misses, 1);
assert_eq!(cache.hits, 0);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_runner_cache_store_and_lookup() {
let dir = create_temp_dir();
let cache_dir = dir.join(".cache");
let mut cache = X86BuildCache::new(&cache_dir);
let source = dir.join("test.c");
let object = dir.join("test.o");
create_file(&source, "int main(){return 0;}");
create_file(&object, "fake object");
let flags = vec!["-O2".to_string(), "-std=c99".to_string()];
cache.store(&source, &object, &flags);
assert!(cache.current_size > 0);
let result = cache.lookup(&source, &flags);
assert!(result.is_some());
assert_eq!(cache.hits, 1);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_runner_different_flags_miss() {
let dir = create_temp_dir();
let cache_dir = dir.join(".cache");
let mut cache = X86BuildCache::new(&cache_dir);
let source = dir.join("test.c");
let object = dir.join("test.o");
create_file(&source, "int main(){return 0;}");
create_file(&object, "fake object");
let flags1 = vec!["-O2".to_string()];
let flags2 = vec!["-O3".to_string()];
cache.store(&source, &object, &flags1);
let result = cache.lookup(&source, &flags2);
assert!(result.is_none());
assert_eq!(cache.misses, 2); cleanup_temp_dir(&dir);
}
#[test]
fn test_build_cache_hash_determinism() {
let data = b"hello world";
let h1 = X86BuildCache::compute_hash(data);
let h2 = X86BuildCache::compute_hash(data);
assert_eq!(h1, h2);
let h3 = X86BuildCache::compute_hash(b"different");
assert_ne!(h1, h3);
}
#[test]
fn test_test_runner_stop_on_failure() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
runner.stop_on_failure = true;
assert!(runner.stop_on_failure);
cleanup_temp_dir(&dir);
}
#[test]
fn test_test_runner_timeout() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
runner.timeout_seconds = Some(30);
assert_eq!(runner.timeout_seconds, Some(30));
cleanup_temp_dir(&dir);
}
#[test]
fn test_test_runner_unknown_build_system() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
let results = runner.run_project_tests("test", &X86BuildSystem::Unknown);
assert!(!results.is_empty());
let suite = &results[0];
assert!(!suite.suite_name.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_ctest_line_parser_pass() {
let line = "Test #1: sample_test ... Passed 1.23 sec";
let result = X86TestRunner::parse_ctest_line(line);
assert!(result.is_some());
assert!(result.unwrap().passed);
}
#[test]
fn test_ctest_line_parser_fail() {
let line = "Test #2: bad_test ... ***Failed 0.45 sec";
let result = X86TestRunner::parse_ctest_line(line);
assert!(result.is_some());
assert!(!result.unwrap().passed);
}
#[test]
fn test_ctest_line_parser_not_test() {
let line = "Running tests...";
let result = X86TestRunner::parse_ctest_line(line);
assert!(result.is_none());
}
#[test]
fn test_junit_xml_empty_suites() {
let dir = create_temp_dir();
let runner = X86TestRunner::new(&dir, &dir, false);
let xml = runner.generate_junit_xml();
assert!(xml.contains("<?xml"));
assert!(xml.contains("<testsuites"));
assert!(xml.contains("tests=\"0\""));
cleanup_temp_dir(&dir);
}
#[test]
fn test_junit_xml_skipped_test() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
runner.results.push(X86TestSuiteResult {
suite_name: "suite".into(),
tests: vec![X86TestResult {
name: "skip_me".into(),
passed: false,
skipped: true,
duration: Duration::ZERO,
stdout: String::new(),
stderr: String::new(),
exit_code: 0,
error_message: Some("disabled".into()),
}],
passed: 0,
failed: 0,
skipped: 1,
total_duration: Duration::ZERO,
});
let xml = runner.generate_junit_xml();
assert!(xml.contains("<skipped"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_sqlite_config_threading_modes() {
let single = X86SQLiteConfig {
threading_mode: 0,
..Default::default()
};
let serialized = X86SQLiteConfig {
threading_mode: 1,
..Default::default()
};
let multi = X86SQLiteConfig {
threading_mode: 2,
..Default::default()
};
assert_eq!(single.threading_mode, 0);
assert_eq!(serialized.threading_mode, 1);
assert_eq!(multi.threading_mode, 2);
for cfg in &[single, serialized, multi] {
let defs = cfg.defines();
let ts_def = defs.iter().find(|(k, _)| k == "SQLITE_THREADSAFE");
assert!(ts_def.is_some());
}
}
#[test]
fn test_sqlite_config_all_features() {
let config = X86SQLiteConfig {
enable_fts5: true,
enable_rtree: true,
enable_json1: true,
enable_geopoly: true,
enable_math_functions: true,
enable_session: true,
enable_dbstat: true,
omit_deprecated: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_FTS5"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_RTREE"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_JSON1"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_GEOPOLY"));
assert!(defs
.iter()
.any(|(k, _)| k == "SQLITE_ENABLE_MATH_FUNCTIONS"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_SESSION"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_ENABLE_DBSTAT_VTAB"));
assert!(defs.iter().any(|(k, _)| k == "SQLITE_OMIT_DEPRECATED"));
}
#[test]
fn test_sqlite_config_simd_level_affects_flags() {
let config = X86SQLiteConfig {
simd_level: X86SIMDLevel::AVX2,
..Default::default()
};
let flags = config.compiler_flags();
assert!(flags.contains(&"-mavx2".to_string()));
assert!(flags.contains(&"-mavx".to_string()));
}
#[test]
fn test_zlib_config_no_asm() {
let config = X86ZlibConfig {
asm_optimizations: false,
gz_trace: false,
};
let defs = config.defines();
assert!(!defs.iter().any(|(k, _)| k == "ASMV"));
}
#[test]
fn test_libpng_no_zlib() {
let config = X86LibpngConfig {
has_zlib: false,
..Default::default()
};
let libs = config.libraries();
assert!(!libs.contains(&"z".to_string()));
let ldflags = config.linker_flags();
assert!(!ldflags.contains(&"-lz".to_string()));
}
#[test]
fn test_libpng_arm_neon_cross_compile() {
let config = X86LibpngConfig {
enable_arm_neon: true,
..Default::default()
};
let defs = config.defines();
let neon_def = defs.iter().find(|(k, _)| k == "PNG_ARM_NEON");
assert_eq!(neon_def.unwrap().1, Some("1".to_string()));
}
#[test]
fn test_jpeg_turbo_12bit_mode() {
let config = X86JpegTurboConfig {
with_12bit: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "WITH_12BIT"));
}
#[test]
fn test_jpeg_turbo_max_mem() {
let config = X86JpegTurboConfig {
max_mem_mgr: Some(1048576),
..Default::default()
};
let defs = config.defines();
let mem_def = defs.iter().find(|(k, _)| k == "MAX_ALLOC_CHUNK");
assert_eq!(mem_def.unwrap().1, Some("1048576".to_string()));
}
#[test]
fn test_openssl_disabled_features() {
let mut config = X86OpenSSLConfig::default();
config.disabled_features.insert("sctp".to_string());
let args = config.emulate_configure();
assert!(args.iter().any(|a| a == "no-sctp"));
}
#[test]
fn test_openssl_no_asm() {
let config = X86OpenSSLConfig {
asm: false,
..Default::default()
};
let args = config.emulate_configure();
assert!(args.iter().any(|a| a == "no-asm"));
}
#[test]
fn test_openssl_version_field() {
let config = X86OpenSSLConfig {
version: "3.2.0".to_string(),
..Default::default()
};
assert_eq!(config.version, "3.2.0");
}
#[test]
fn test_curl_http3_requires_extra_deps() {
let config = X86CurlConfig {
enable_http3: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "USE_NGTCP2"));
}
#[test]
fn test_curl_all_disabled() {
let config = X86CurlConfig {
enable_http: false,
enable_ssl: false,
enable_ftp: false,
enable_cookies: false,
enable_ipv6: false,
enable_unix_sockets: false,
with_zlib: false,
..Default::default()
};
let defs = config.defines();
assert!(!defs.iter().any(|(k, _)| k == "USE_HTTP"));
assert!(!defs.iter().any(|(k, _)| k == "USE_SSL"));
assert!(!defs.iter().any(|(k, _)| k == "HAVE_LIBZ"));
}
#[test]
fn test_nginx_module_system() {
let config = X86NginxConfig::default();
assert_eq!(config.modules.len(), 3);
let ssl_mod = config.modules.iter().find(|m| m.name.contains("ssl"));
assert!(ssl_mod.is_some());
assert!(ssl_mod
.unwrap()
.defines
.iter()
.any(|(k, _)| k == "NGX_HTTP_SSL"));
}
#[test]
fn test_nginx_with_all_disabled() {
let config = X86NginxConfig {
http: false,
mail: true,
stream: true,
with_ssl: false,
..Default::default()
};
let defs = config.defines();
assert!(!defs.iter().any(|(k, _)| k == "NGX_HTTP"));
}
#[test]
fn test_redis_malloc_variants() {
let jemalloc = X86RedisConfig {
malloc: X86RedisMalloc::Jemalloc,
use_jemalloc: true,
..Default::default()
};
let libc = X86RedisConfig {
malloc: X86RedisMalloc::Libc,
use_jemalloc: false,
..Default::default()
};
assert!(jemalloc.defines().iter().any(|(k, _)| k == "USE_JEMALLOC"));
assert!(!libc.defines().iter().any(|(k, _)| k == "USE_JEMALLOC"));
}
#[test]
fn test_redis_tls_config() {
let config = X86RedisConfig {
build_tls: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "USE_OPENSSL"));
let ldflags = config.linker_flags();
assert!(ldflags.contains(&"-lssl".to_string()));
}
#[test]
fn test_lua_32bit_numbers() {
let config = X86LuaConfig {
use_32bit_numbers: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "LUA_32BITS"));
}
#[test]
fn test_lua_float_variants() {
let float_cfg = X86LuaConfig {
float_width: X86LuaFloat::Float,
..Default::default()
};
let double_cfg = X86LuaConfig {
float_width: X86LuaFloat::Double,
..Default::default()
};
let longdouble_cfg = X86LuaConfig {
float_width: X86LuaFloat::LongDouble,
..Default::default()
};
let float_defs = float_cfg.defines();
assert!(float_defs
.iter()
.any(|(k, v)| k == "LUA_FLOAT_TYPE" && v == &Some("float".to_string())));
let double_defs = double_cfg.defines();
assert!(!double_defs.iter().any(|(k, _)| k == "LUA_FLOAT_TYPE"));
let ld_defs = longdouble_cfg.defines();
assert!(ld_defs.iter().any(|(k, _)| k == "LUA_FLOAT_LONGDOUBLE"));
}
#[test]
fn test_lua_no_readline() {
let config = X86LuaConfig {
with_readline: false,
..Default::default()
};
let ldflags = config.linker_flags();
assert!(!ldflags.contains(&"-lreadline".to_string()));
}
#[test]
fn test_jsonc_threading_off() {
let config = X86JsonCConfig {
enable_threading: false,
..Default::default()
};
let defs = config.defines();
assert!(!defs.iter().any(|(k, _)| k == "ENABLE_THREADING"));
let ldflags = config.linker_flags();
assert!(!ldflags.contains(&"-lpthread".to_string()));
}
#[test]
fn test_stb_multiple_libraries() {
let config = X86StbConfig {
libraries: vec![
X86StbLibrary::Image,
X86StbLibrary::ImageWrite,
X86StbLibrary::TrueType,
X86StbLibrary::RectanglePack,
X86StbLibrary::Perlin,
],
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "STB_IMAGE_IMPLEMENTATION"));
assert!(defs
.iter()
.any(|(k, _)| k == "STB_IMAGE_WRITE_IMPLEMENTATION"));
assert!(defs.iter().any(|(k, _)| k == "STB_TRUETYPE_IMPLEMENTATION"));
assert!(defs
.iter()
.any(|(k, _)| k == "STB_RECT_PACK_IMPLEMENTATION"));
assert!(defs.iter().any(|(k, _)| k == "STB_PERLIN_IMPLEMENTATION"));
}
#[test]
fn test_fmtlib_unicode_and_wchar() {
let config = X86FmtlibConfig {
with_wchar: true,
with_unicode: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "FMT_USE_WCHAR"));
assert!(defs.iter().any(|(k, _)| k == "FMT_UNICODE"));
}
#[test]
fn test_spdlog_async_mode() {
let config = X86SpdlogConfig {
enable_async: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "SPDLOG_ACTIVE_LEVEL"));
let ldflags = config.linker_flags();
assert!(ldflags.contains(&"-lpthread".to_string()));
}
#[test]
fn test_spdlog_external_fmt() {
let config = X86SpdlogConfig {
use_external_fmt: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "SPDLOG_FMT_EXTERNAL"));
}
#[test]
fn test_spdlog_syslog() {
let config = X86SpdlogConfig {
enable_syslog: true,
..Default::default()
};
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "SPDLOG_ENABLE_SYSLOG"));
}
#[test]
fn test_pkgconfig_default_search_paths() {
let pc = X86PackageConfig::new();
assert!(pc.search_paths.iter().any(|p| p.ends_with("pkgconfig")));
assert!(pc.search_paths.iter().any(|p| p.starts_with("/usr")));
}
#[test]
fn test_pkgconfig_parse_variables() {
let content = "prefix=/usr\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\n";
let vars = X86PackageConfig::parse_variables(content);
assert_eq!(vars.get("prefix").map(|s| s.as_str()), Some("/usr"));
assert_eq!(
vars.get("exec_prefix").map(|s| s.as_str()),
Some("${prefix}")
);
assert_eq!(
vars.get("libdir").map(|s| s.as_str()),
Some("${exec_prefix}/lib")
);
}
#[test]
fn test_pkgconfig_parse_kv() {
assert_eq!(
X86PackageConfig::parse_kv("Name: zlib"),
Some(("Name", "zlib")),
);
assert_eq!(
X86PackageConfig::parse_kv("Version: 1.3.1"),
Some(("Version", "1.3.1")),
);
assert_eq!(X86PackageConfig::parse_kv("no colon here"), None);
}
#[test]
fn test_pkgconfig_parse_flags_complex() {
let flags = X86PackageConfig::parse_flags(
"-I/usr/include/foo -I/usr/local/include -DHAVE_CONFIG -O2",
);
assert_eq!(flags.len(), 4);
}
#[test]
fn test_pkgconfig_parse_flags_empty() {
let flags = X86PackageConfig::parse_flags("");
assert!(flags.is_empty());
}
#[test]
fn test_pkgconfig_version_check_edge_cases() {
assert!(X86PackageConfig::check_version("10.0", "> 9.0").unwrap());
assert!(X86PackageConfig::check_version("10.0", ">= 10.0").unwrap());
assert!(!X86PackageConfig::check_version("9.0", "> 10.0").unwrap());
assert!(X86PackageConfig::check_version("1", "== 1").unwrap());
assert!(X86PackageConfig::check_version("2", "> 1").unwrap());
}
#[test]
fn test_pkgconfig_combined_libs_no_duplicates() {
let mut deps = Vec::new();
deps.push(X86PcFileInfo {
name: "a".into(),
version: "1.0".into(),
description: String::new(),
prefix: PathBuf::from("/usr"),
include_dirs: Vec::new(),
lib_dirs: vec![PathBuf::from("/usr/lib")],
libs: vec!["z".into(), "m".into()],
cflags: Vec::new(),
requires: Vec::new(),
requires_private: Vec::new(),
});
deps.push(X86PcFileInfo {
name: "b".into(),
version: "2.0".into(),
description: String::new(),
prefix: PathBuf::from("/usr"),
include_dirs: Vec::new(),
lib_dirs: vec![PathBuf::from("/usr/lib")],
libs: vec!["z".into()], cflags: Vec::new(),
requires: Vec::new(),
requires_private: Vec::new(),
});
let libs = X86PackageConfig::combined_libs(&deps);
let z_count = libs.iter().filter(|l| *l == "-lz").count();
assert_eq!(z_count, 1, "Should deduplicate -lz");
let m_count = libs.iter().filter(|l| *l == "-lm").count();
assert_eq!(m_count, 1, "Should have one -lm");
}
#[test]
fn test_pkgconfig_resolve_requires_cycle_detection() {
let dir = create_temp_dir();
let pkgconfig_dir = dir.join("pkgconfig");
fs::create_dir_all(&pkgconfig_dir).unwrap();
create_file(
&pkgconfig_dir.join("a.pc"),
"prefix=/usr\nName: a\nVersion: 1\nDescription: A\nRequires: b\nLibs: -la",
);
create_file(
&pkgconfig_dir.join("b.pc"),
"prefix=/usr\nName: b\nVersion: 1\nDescription: B\nRequires: a\nLibs: -lb",
);
let mut pc = X86PackageConfig::new();
pc.add_search_path(&pkgconfig_dir);
let result = pc.resolve_requires("a");
assert!(result.is_ok());
let deps = result.unwrap();
assert!(deps.len() >= 2);
cleanup_temp_dir(&dir);
}
#[test]
fn test_x86_64_to_x86_32_cross_compile() {
let dir = create_temp_dir();
create_file(&dir.join("Makefile"), "CC=gcc");
let mut compiler = X86RealProjectCompiler::x86_32_linux(&dir);
assert_eq!(compiler.target_triple, X86_32_LINUX_TRIPLE);
compiler.detect_project().ok();
let report = compiler.generate_report();
assert!(report.contains("i686"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_windows_target_configuration() {
let dir = create_temp_dir();
create_file(&dir.join("CMakeLists.txt"), "project(win_test)");
let compiler = X86RealProjectCompiler::x86_64_windows(&dir);
assert_eq!(compiler.target_triple, X86_64_WINDOWS_TRIPLE);
assert_eq!(
X86ConfigureDetector::detect_platform(&compiler.target_triple),
X86Platform::Windows,
);
cleanup_temp_dir(&dir);
}
#[test]
fn test_project_config_none_on_init() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::new(&dir);
assert!(compiler.project_config.is_none());
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_empty_project() {
let dir = create_temp_dir();
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::Unknown);
assert!(detector.collect_sources().is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_deeply_nested_sources() {
let dir = create_temp_dir();
let nested = dir.join("src").join("core").join("impl");
fs::create_dir_all(&nested).unwrap();
create_file(&dir.join("CMakeLists.txt"), "project(nested)");
create_file(&nested.join("deep.c"), "int deep() { return 42; }");
let detector = X86ProjectDetector::scan(&dir);
assert_eq!(detector.build_system, X86BuildSystem::CMake);
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_detect_then_report_empty() {
let dir = create_temp_dir();
let mut compiler = X86RealProjectCompiler::new(&dir);
compiler.detect_project().ok();
let report = compiler.generate_report();
assert!(report.contains("X86 Real Project"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_result_default_fields() {
let result = X86BuildResult {
file: PathBuf::from("test.c"),
success: true,
duration: Duration::from_millis(100),
object_file: Some(PathBuf::from("test.o")),
errors: Vec::new(),
warnings: Vec::new(),
exit_code: 0,
stdout: String::new(),
stderr: String::new(),
};
assert!(result.success);
assert_eq!(result.exit_code, 0);
assert!(result.object_file.is_some());
}
#[test]
fn test_build_result_failure() {
let result = X86BuildResult {
file: PathBuf::from("bad.c"),
success: false,
duration: Duration::from_millis(50),
object_file: None,
errors: vec![X86CompileDiagnostic {
severity: X86DiagLevel::Error,
message: "expected ';'".into(),
file: Some(PathBuf::from("bad.c")),
line: Some(42),
column: Some(10),
code: Some("E0001".into()),
fixits: vec!["insert ';'".into()],
}],
warnings: Vec::new(),
exit_code: 1,
stdout: String::new(),
stderr: "error: expected ';'".into(),
};
assert!(!result.success);
assert_eq!(result.errors.len(), 1);
assert_eq!(result.errors[0].line, Some(42));
assert_eq!(result.errors[0].column, Some(10));
assert_eq!(result.errors[0].code.as_deref(), Some("E0001"));
assert_eq!(result.errors[0].fixits.len(), 1);
}
#[test]
fn test_compiler_with_features() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::new(&dir).with_features(&["avx2", "fma", "bmi2"]);
assert_eq!(compiler.target_features.len(), 3);
assert!(compiler.target_features.contains(&"avx2".to_string()));
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_with_target_triple() {
let dir = create_temp_dir();
let compiler =
X86RealProjectCompiler::new(&dir).with_target_triple("x86_64-unknown-freebsd");
assert_eq!(compiler.target_triple, "x86_64-unknown-freebsd");
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_with_target_cpu() {
let dir = create_temp_dir();
let compiler = X86RealProjectCompiler::new(&dir).with_target_cpu("znver4");
assert_eq!(compiler.target_cpu, "znver4");
cleanup_temp_dir(&dir);
}
#[test]
fn test_simd_level_str_consistency() {
let levels = [
X86SIMDLevel::None,
X86SIMDLevel::MMX,
X86SIMDLevel::SSE,
X86SIMDLevel::SSE2,
X86SIMDLevel::SSE3,
X86SIMDLevel::SSSE3,
X86SIMDLevel::SSE41,
X86SIMDLevel::SSE42,
X86SIMDLevel::AVX,
X86SIMDLevel::AVX2,
X86SIMDLevel::AVX512F,
];
for level in &levels {
let s = level.as_str();
assert!(!s.is_empty());
let _ = level.compile_flags();
}
}
#[test]
fn test_all_microarchitectures_listed() {
assert!(
X86_MICROARCHS.len() > 10,
"Should have many microarchitectures"
);
assert!(X86_MICROARCHS.contains(&"skylake"));
assert!(X86_MICROARCHS.contains(&"znver4"));
assert!(X86_MICROARCHS.contains(&"x86-64-v3"));
}
#[test]
fn test_all_cpu_features_listed() {
assert!(X86_CPU_FEATURES.len() > 5);
assert!(X86_CPU_FEATURES.contains(&"sse2"));
assert!(X86_CPU_FEATURES.contains(&"avx512f"));
}
#[test]
fn test_configure_all_compiler_flags() {
let config = X86BuildConfiguration {
defines: vec![("FOO".into(), Some("1".into()))],
include_dirs: vec![PathBuf::from("/include")],
lib_dirs: vec![PathBuf::from("/lib")],
libraries: vec!["z".into()],
cflags: vec!["-O2".into()],
cxxflags: vec!["-std=c++17".into()],
ldflags: vec!["-Wl,-rpath,/lib".into()],
arch_flags: vec!["-m64".into()],
platform: X86Platform::Linux,
architecture: X86ArchVariant::X86_64,
features: X86Features::default(),
language_standard: "c11".into(),
use_exceptions: Some(true),
use_rtti: Some(true),
use_lto: Some(false),
simd_level: X86SIMDLevel::SSE2,
};
let compiler_flags = config.all_compiler_flags();
assert!(compiler_flags.contains(&"-O2".to_string()));
assert!(compiler_flags.contains(&"-std=c++17".to_string()));
assert!(compiler_flags.contains(&"-m64".to_string()));
assert!(compiler_flags.contains(&"-DFOO=1".to_string()));
assert!(compiler_flags.contains(&"-I/include".to_string()));
let linker_flags = config.all_linker_flags();
assert!(linker_flags.contains(&"-L/lib".to_string()));
assert!(linker_flags.contains(&"-lz".to_string()));
}
#[test]
fn test_dependency_resolver_full_map() {
let map = X86DependencyMap::default();
assert!(map.dependencies.is_empty());
assert!(map.header_libs.is_empty());
assert!(map.system_packages.is_empty());
assert!(map.pkgconfig_modules.is_empty());
assert!(map.git_submodules.is_empty());
}
#[test]
fn test_dependency_discovery_source_display() {
let sources = vec![
X86DepDiscoverySource::PkgConfig("zlib".into()),
X86DepDiscoverySource::CMakeFindPackage,
X86DepDiscoverySource::SystemPackage,
X86DepDiscoverySource::HeaderOnly,
X86DepDiscoverySource::GitSubmodule,
X86DepDiscoverySource::Manual,
X86DepDiscoverySource::NotFound,
];
for src in &sources {
let _ = format!("{:?}", src);
}
}
#[test]
fn test_build_cache_compute_hash_consistency() {
let empty_hash = X86BuildCache::compute_hash(b"");
let same_hash = X86BuildCache::compute_hash(b"");
assert_eq!(empty_hash, same_hash);
let a_hash = X86BuildCache::compute_hash(b"a");
let b_hash = X86BuildCache::compute_hash(b"b");
assert_ne!(a_hash, b_hash);
}
#[test]
fn test_cache_stats_default() {
let dir = create_temp_dir();
let cache = X86BuildCache::new(&dir.join(".cache"));
let stats = cache.stats();
assert!(stats.contains("0 hits"));
assert!(stats.contains("0.0%"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_project_outcome_clone() {
let outcome = X86ProjectOutcome {
project_name: "test".into(),
build_success: true,
test_success: false,
build_results: vec![],
test_results: vec![],
total_duration: Duration::from_secs(1),
};
let cloned = outcome.clone();
assert_eq!(cloned.project_name, outcome.project_name);
assert_eq!(cloned.build_success, outcome.build_success);
assert_eq!(cloned.test_success, outcome.test_success);
}
#[test]
fn test_diagnostic_with_full_details() {
let diag = X86CompileDiagnostic {
severity: X86DiagLevel::Error,
message: "type mismatch: expected int, found float".into(),
file: Some(PathBuf::from("src/main.c")),
line: Some(100),
column: Some(25),
code: Some("E_TYPE_MISMATCH".into()),
fixits: vec!["change type to int".into(), "add cast".into()],
};
assert_eq!(diag.severity, X86DiagLevel::Error);
assert_eq!(diag.line, Some(100));
assert_eq!(diag.fixits.len(), 2);
}
#[test]
fn test_package_manager_all_have_install_commands() {
let managers = [
X86PackageManager::Apt,
X86PackageManager::Yum,
X86PackageManager::Brew,
X86PackageManager::Pacman,
X86PackageManager::Zypper,
X86PackageManager::Portage,
X86PackageManager::Nix,
X86PackageManager::Pkg,
];
for mgr in &managers {
let cmd = mgr.install_command("test-package");
assert!(cmd.is_some(), "{:?} should have install command", mgr);
}
assert!(X86PackageManager::Unknown.install_command("test").is_none());
}
#[test]
fn test_git_submodule_detection() {
let dir = create_temp_dir();
create_file(
&dir.join(".gitmodules"),
"[submodule \"libfoo\"]\n\tpath = third_party/foo\n\turl = https://github.com/example/foo.git\n[submodule \"libbar\"]\n\tpath = third_party/bar\n\turl = https://github.com/example/bar.git",
);
let subs = X86DependencyResolver::detect_git_submodules(&dir);
assert_eq!(subs.len(), 2);
assert!(subs.iter().any(|s| s.path == "third_party/foo"));
assert!(subs.iter().any(|s| s.url.contains("bar.git")));
cleanup_temp_dir(&dir);
}
#[test]
fn test_git_submodule_no_file() {
let dir = create_temp_dir();
let subs = X86DependencyResolver::detect_git_submodules(&dir);
assert!(subs.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_custom_discovery_no_executables() {
let dir = create_temp_dir();
let runner = X86TestRunner::new(&dir, &dir, false);
let result = runner.run_custom_discovery("empty_suite");
assert!(!result.tests.is_empty());
assert!(result.tests[0].skipped);
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_version_from_file() {
let dir = create_temp_dir();
create_file(&dir.join("VERSION"), "3.14.0");
let version =
X86ProjectDetector::detect_version(&dir, X86BuildSystem::Unknown, &HashMap::new());
assert_eq!(version, Some("3.14.0".to_string()));
cleanup_temp_dir(&dir);
}
#[test]
fn test_detect_description_from_readme() {
let dir = create_temp_dir();
create_file(
&dir.join("README.md"),
"# A Cool Project\n\nThis is a description.",
);
let desc =
X86ProjectDetector::detect_description(&dir, X86BuildSystem::Unknown, &HashMap::new());
assert_eq!(desc, Some("A Cool Project".to_string()));
cleanup_temp_dir(&dir);
}
#[test]
fn test_discover_sub_projects() {
let dir = create_temp_dir();
let sub = dir.join("lib");
fs::create_dir_all(&sub).unwrap();
create_file(&sub.join("CMakeLists.txt"), "project(lib)");
let subs = X86ProjectDetector::discover_sub_projects(&dir);
assert!(!subs.is_empty());
let lib_sub = subs.iter().find(|s| s.project_root == sub);
assert!(lib_sub.is_some());
cleanup_temp_dir(&dir);
}
#[test]
fn test_compiler_detect_project_with_sources() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"project(Test)\nadd_executable(test main.c)",
);
create_file(&dir.join("main.c"), "int main(void) { return 0; }");
create_file(&dir.join("include").join("test.h"), "#pragma once");
let mut compiler = X86RealProjectCompiler::new(&dir);
let config = compiler.detect_project().unwrap();
assert!(!config.sources.is_empty());
assert!(!config.includes.is_empty());
assert!(!config.name.is_empty());
let report = compiler.generate_report();
assert!(report.contains("cmake"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_config_detect_language_standard_from_cmake() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"set(CMAKE_C_STANDARD 11)\nproject(test)",
);
let detector = X86ProjectDetector::scan(&dir);
let standard = X86ConfigureDetector::detect_language_standard(&dir, &detector);
assert_eq!(standard, "c11");
cleanup_temp_dir(&dir);
}
#[test]
fn test_config_detect_exceptions() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"project(test)\nset(CMAKE_CXX_EXCEPTIONS ON)",
);
let has_exc = X86ConfigureDetector::detect_exceptions(&dir);
assert_eq!(has_exc, Some(true));
cleanup_temp_dir(&dir);
}
#[test]
fn test_config_detect_lto() {
let dir = create_temp_dir();
create_file(
&dir.join("CMakeLists.txt"),
"project(test)\nset(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)",
);
let has_lto = X86ConfigureDetector::detect_lto(&dir);
assert_eq!(has_lto, Some(true));
cleanup_temp_dir(&dir);
}
#[test]
fn test_many_defines_handling() {
let dir = create_temp_dir();
create_file(
&dir.join("config.h"),
(0..200)
.map(|i| format!("#define HAVE_FEATURE_{} 1", i))
.collect::<Vec<_>>()
.join("\n")
.as_str(),
);
let detector = X86ProjectDetector::scan(&dir);
let defines = X86ConfigureDetector::detect_defines(
&dir,
&detector,
X86Platform::Linux,
X86ArchVariant::X86_64,
&X86Features::default(),
);
assert!(defines.len() >= 200);
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_summary_with_many_files() {
let dir = create_temp_dir();
let mut runner = X86BuildRunner::new(&dir, 1, false);
runner.total_files = 500;
runner.succeeded = 498;
runner.failed = 2;
let summary = runner.build_summary();
assert!(summary.contains("Total files: 500"));
assert!(summary.contains("Succeeded: 498"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_large_test_suite_junit() {
let dir = create_temp_dir();
let mut runner = X86TestRunner::new(&dir, &dir, false);
let mut tests = Vec::new();
for i in 0..100 {
tests.push(X86TestResult {
name: format!("test_{:03}", i),
passed: i % 3 != 0,
skipped: i % 10 == 0,
duration: Duration::from_micros(100 + i * 10),
stdout: String::new(),
stderr: String::new(),
exit_code: if i % 3 != 0 { 0 } else { 1 },
error_message: if i % 3 == 0 {
Some("failed".into())
} else {
None
},
});
}
let passed = tests.iter().filter(|t| t.passed).count();
let failed = tests.iter().filter(|t| !t.passed && !t.skipped).count();
let skipped = tests.iter().filter(|t| t.skipped).count();
runner.results.push(X86TestSuiteResult {
suite_name: "big_suite".into(),
tests,
passed,
failed,
skipped,
total_duration: Duration::from_secs(5),
});
let xml = runner.generate_junit_xml();
assert!(xml.contains("tests=\"100\""));
assert!(xml.contains("test_000"));
assert!(xml.contains("test_099"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_project_name_from_path() {
let dir = create_temp_dir();
let detector = X86ProjectDetector::scan(&dir);
assert!(!detector.project_name().is_empty());
assert_eq!(
detector.project_name(),
dir.file_name().unwrap().to_str().unwrap()
);
cleanup_temp_dir(&dir);
}
#[test]
fn test_target_kind_all_variants() {
let kinds = [
X86TargetKind::Executable,
X86TargetKind::StaticLibrary,
X86TargetKind::SharedLibrary,
X86TargetKind::HeaderOnly,
X86TargetKind::ObjectFile,
X86TargetKind::Custom,
];
for kind in &kinds {
let s = kind.as_str();
assert!(!s.is_empty());
}
}
#[test]
fn test_build_system_all_variants_coverage() {
let systems = [
X86BuildSystem::CMake,
X86BuildSystem::Make,
X86BuildSystem::Autotools,
X86BuildSystem::Meson,
X86BuildSystem::Bazel,
X86BuildSystem::Cargo,
X86BuildSystem::Ninja,
X86BuildSystem::SCons,
X86BuildSystem::Custom,
X86BuildSystem::Unknown,
];
for sys in &systems {
let s = sys.as_str();
assert!(!s.is_empty());
let _ = sys.default_build_file();
}
}
#[test]
fn test_no_panic_on_missing_files() {
let dir = create_temp_dir();
let configs: Vec<Box<dyn X86ProjectConfigurable>> = vec![
Box::new(X86SQLiteConfig::default()),
Box::new(X86ZlibConfig::default()),
Box::new(X86LibpngConfig::default()),
Box::new(X86JpegTurboConfig::default()),
Box::new(X86RedisConfig::default()),
Box::new(X86LuaConfig::default()),
Box::new(X86JsonCConfig::default()),
];
for config in &configs {
let sources = config.sources(&dir);
let _ = sources;
let includes = config.includes(&dir);
let _ = includes;
}
cleanup_temp_dir(&dir);
}
}