use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::fs;
use std::io::{self, BufRead, BufReader, Read, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::clang::clang_real_projects_x86::{
self, compile_to_x86_object, compile_with_options, CompilationCache, PipelineResult,
X86ArchVariant, X86BuildCache, X86BuildConfiguration, X86BuildResult, X86BuildRunner,
X86BuildSystem, X86CompilationDatabase, X86CompileDiagnostic, X86CompileEntry,
X86CompileOptions, X86ConfigureDetector, X86Dependency, X86DependencyMap,
X86DependencyResolver, X86DiagLevel, X86Features, X86OptLevel, X86OutputFormat, X86Pipeline,
X86Platform, X86ProjectConfig, X86ProjectConfigurable, X86ProjectDetector, X86ProjectOutcome,
X86RealProjectCompiler, X86SIMDLevel, X86TestResult, X86TestRunner, X86TestSuiteResult,
DEFAULT_CACHE_DIR, DEFAULT_CACHE_MAX_SIZE, DEFAULT_PARALLEL_JOBS, X86_32_LINUX_TRIPLE,
X86_64_DARWIN_TRIPLE, X86_64_LINUX_TRIPLE, X86_64_WINDOWS_TRIPLE, X86_CPU_FEATURES,
X86_MICROARCHS,
};
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 EXTRA_PROJECT_NAMES: &[&str] = &[
"postgresql",
"mysql",
"mongodb",
"redis",
"memcached",
"nginx",
"httpd",
"node",
"cpython",
"ruby",
"php",
"v8",
"spidermonkey",
"webkit",
"chromium",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExtraProjectCategory {
Database,
WebServer,
LanguageRuntime,
Browser,
Cache,
}
impl ExtraProjectCategory {
pub fn as_str(&self) -> &'static str {
match self {
Self::Database => "database",
Self::WebServer => "web-server",
Self::LanguageRuntime => "language-runtime",
Self::Browser => "browser",
Self::Cache => "cache",
}
}
}
impl fmt::Display for ExtraProjectCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct ExtraProjectVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub pre_release: Option<String>,
pub build_metadata: Option<String>,
}
impl ExtraProjectVersion {
pub fn new(major: u32, minor: u32, patch: u32) -> Self {
Self {
major,
minor,
patch,
pre_release: None,
build_metadata: None,
}
}
pub fn parse(s: &str) -> Option<Self> {
let parts: Vec<&str> = s.split(|c: char| !c.is_ascii_digit() && c != '.').collect();
if parts.len() < 2 || parts[0].is_empty() {
return None;
}
let nums: Vec<&str> = parts[0].split('.').collect();
if nums.len() < 3 {
return None;
}
let major = nums[0].parse().ok()?;
let minor = nums[1].parse().ok()?;
let patch = nums[2].parse().ok()?;
Some(Self {
major,
minor,
patch,
pre_release: None,
build_metadata: None,
})
}
pub fn to_string(&self) -> String {
let mut s = format!("{}.{}.{}", self.major, self.minor, self.patch);
if let Some(ref pre) = self.pre_release {
s.push_str(&format!("-{}", pre));
}
if let Some(ref meta) = self.build_metadata {
s.push_str(&format!("+{}", meta));
}
s
}
}
pub struct X86ProjectsExtra {
pub compiler: X86RealProjectCompiler,
pub configs: HashMap<String, Box<dyn X86ProjectConfigurable>>,
pub categories: HashMap<String, ExtraProjectCategory>,
pub versions: HashMap<String, ExtraProjectVersion>,
pub build_history: Vec<ExtraBuildRecord>,
pub compare: Option<X86ProjectCompare>,
pub patch_manager: X86ProjectPatch,
pub container_manager: X86ProjectContainer,
pub verbose: bool,
}
#[derive(Debug, Clone)]
pub struct ExtraBuildRecord {
pub project_name: String,
pub timestamp: SystemTime,
pub duration: Duration,
pub success: bool,
pub compiler_used: Option<String>,
pub binary_size: Option<u64>,
pub error_count: usize,
pub warning_count: usize,
}
impl X86ProjectsExtra {
pub fn new(project_root: &Path) -> Self {
let compiler = X86RealProjectCompiler::x86_64_linux(project_root);
Self {
compiler,
configs: HashMap::new(),
categories: HashMap::new(),
versions: HashMap::new(),
build_history: Vec::new(),
compare: None,
patch_manager: X86ProjectPatch::new(project_root),
container_manager: X86ProjectContainer::new(project_root),
verbose: false,
}
}
pub fn register(
&mut self,
config: Box<dyn X86ProjectConfigurable>,
category: ExtraProjectCategory,
version: ExtraProjectVersion,
) {
let name = config.name().to_string();
self.categories.insert(name.clone(), category);
self.versions.insert(name.clone(), version);
self.configs.insert(name, config);
}
pub fn register_all(&mut self) {
self.register(
Box::new(X86PostgreSQLConfig::default()),
ExtraProjectCategory::Database,
ExtraProjectVersion::new(16, 3, 0),
);
self.register(
Box::new(X86MySQLConfig::default()),
ExtraProjectCategory::Database,
ExtraProjectVersion::new(8, 0, 36),
);
self.register(
Box::new(X86MongoDBConfig::default()),
ExtraProjectCategory::Database,
ExtraProjectVersion::new(7, 0, 5),
);
self.register(
Box::new(X86RedisExtraConfig::default()),
ExtraProjectCategory::Cache,
ExtraProjectVersion::new(7, 2, 4),
);
self.register(
Box::new(X86MemcachedConfig::default()),
ExtraProjectCategory::Cache,
ExtraProjectVersion::new(1, 6, 24),
);
self.register(
Box::new(X86NginxExtraConfig::default()),
ExtraProjectCategory::WebServer,
ExtraProjectVersion::new(1, 25, 3),
);
self.register(
Box::new(X86ApacheHttpdConfig::default()),
ExtraProjectCategory::WebServer,
ExtraProjectVersion::new(2, 4, 59),
);
self.register(
Box::new(X86NodeJsConfig::default()),
ExtraProjectCategory::LanguageRuntime,
ExtraProjectVersion::new(22, 2, 0),
);
self.register(
Box::new(X86CPythonConfig::default()),
ExtraProjectCategory::LanguageRuntime,
ExtraProjectVersion::new(3, 12, 3),
);
self.register(
Box::new(X86RubyConfig::default()),
ExtraProjectCategory::LanguageRuntime,
ExtraProjectVersion::new(3, 3, 1),
);
self.register(
Box::new(X86PhpConfig::default()),
ExtraProjectCategory::LanguageRuntime,
ExtraProjectVersion::new(8, 3, 7),
);
self.register(
Box::new(X86V8Config::default()),
ExtraProjectCategory::Browser,
ExtraProjectVersion::new(12, 5, 0),
);
self.register(
Box::new(X86SpiderMonkeyConfig::default()),
ExtraProjectCategory::Browser,
ExtraProjectVersion::new(126, 0, 0),
);
self.register(
Box::new(X86WebKitConfig::default()),
ExtraProjectCategory::Browser,
ExtraProjectVersion::new(2, 44, 1),
);
self.register(
Box::new(X86ChromiumConfig::default()),
ExtraProjectCategory::Browser,
ExtraProjectVersion::new(126, 0, 0),
);
}
pub fn build_all(&mut self) -> HashMap<String, Result<Vec<X86BuildResult>, String>> {
let mut results = HashMap::new();
let names: Vec<String> = self.configs.keys().cloned().collect();
for name in &names {
let result = self.build_one(name);
results.insert(name.clone(), result);
}
results
}
pub fn build_one(&mut self, name: &str) -> Result<Vec<X86BuildResult>, String> {
let config = self
.configs
.get(name)
.ok_or_else(|| format!("Project '{}' not registered", name))?;
let root = self.compiler.project_root.clone();
let sources = config.sources(&root);
let defines = config.defines();
let includes = config.includes(&root);
let compiler_flags = config.compiler_flags();
let start = Instant::now();
let mut runner = X86BuildRunner::new(
&self.compiler.build_dir,
DEFAULT_PARALLEL_JOBS,
self.verbose,
);
let results = runner.compile_project(
&sources,
&includes,
&defines,
&compiler_flags,
&self.compiler.options,
)?;
let duration = start.elapsed();
let success = results.iter().all(|r| r.success);
let error_count: usize = results.iter().map(|r| r.errors.len()).sum();
let warning_count: usize = results.iter().map(|r| r.warnings.len()).sum();
self.build_history.push(ExtraBuildRecord {
project_name: name.to_string(),
timestamp: SystemTime::now(),
duration,
success,
compiler_used: Some("clang-x86".into()),
binary_size: None,
error_count,
warning_count,
});
Ok(results)
}
pub fn category(&self, name: &str) -> Option<ExtraProjectCategory> {
self.categories.get(name).copied()
}
pub fn version(&self, name: &str) -> Option<&ExtraProjectVersion> {
self.versions.get(name)
}
pub fn list_projects(&self) -> Vec<String> {
let mut names: Vec<String> = self.configs.keys().cloned().collect();
names.sort();
names
}
pub fn list_by_category(&self, category: ExtraProjectCategory) -> Vec<String> {
let mut names: Vec<String> = self
.categories
.iter()
.filter(|(_, cat)| **cat == category)
.map(|(name, _)| name.clone())
.collect();
names.sort();
names
}
pub fn generate_extended_report(&self) -> String {
let mut report = String::new();
report.push_str("═══════════════════════════════════════════════════════════════\n");
report.push_str(" X86 Extended Projects Build Report\n");
report.push_str("═══════════════════════════════════════════════════════════════\n\n");
for name in self.list_projects() {
let cat = self
.category(&name)
.map(|c| c.as_str().to_string())
.unwrap_or_else(|| "unknown".into());
let ver = self
.version(&name)
.map(|v| v.to_string())
.unwrap_or_else(|| "?".into());
report.push_str(&format!(" {:20} [{:18}] v{}\n", name, cat, ver));
}
report.push_str("\n── Build History ───────────────────────────────────────────\n");
for record in &self.build_history {
let status = if record.success { "✓" } else { "✗" };
report.push_str(&format!(
" {} {} {:>8}ms errors: {} warnings: {}\n",
status,
record.project_name,
record.duration.as_millis(),
record.error_count,
record.warning_count,
));
}
report
}
}
pub struct X86PostgreSQLConfig {
pub with_icu: bool,
pub icu_include: Option<PathBuf>,
pub with_ssl: bool,
pub ssl_include: Option<PathBuf>,
pub with_llvm: bool,
pub llvm_config: Option<PathBuf>,
pub with_libxml: bool,
pub with_libxslt: bool,
pub with_systemd: bool,
pub with_readline: bool,
pub with_zlib: bool,
pub with_lz4: bool,
pub with_zstd: bool,
pub with_pam: bool,
pub with_ldap: bool,
pub with_gssapi: bool,
pub enable_nls: bool,
pub enable_debug: bool,
pub enable_cassert: bool,
pub enable_dtrace: bool,
pub blocksize: u32,
pub segsize: u32,
pub wal_blocksize: u32,
pub wal_segsize: u32,
pub simd_level: X86SIMDLevel,
}
impl Default for X86PostgreSQLConfig {
fn default() -> Self {
Self {
with_icu: true,
icu_include: None,
with_ssl: true,
ssl_include: None,
with_llvm: true,
llvm_config: None,
with_libxml: false,
with_libxslt: false,
with_systemd: false,
with_readline: true,
with_zlib: true,
with_lz4: false,
with_zstd: false,
with_pam: false,
with_ldap: false,
with_gssapi: false,
enable_nls: false,
enable_debug: false,
enable_cassert: false,
enable_dtrace: false,
blocksize: 8192,
segsize: 1,
wal_blocksize: 8192,
wal_segsize: 16,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86PostgreSQLConfig {
fn name(&self) -> &str {
"postgresql"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src");
let dirs = [
"backend/access/brin",
"backend/access/common",
"backend/access/gin",
"backend/access/gist",
"backend/access/hash",
"backend/access/heap",
"backend/access/index",
"backend/access/nbtree",
"backend/access/rmgrdesc",
"backend/access/spgist",
"backend/access/table",
"backend/access/tablesample",
"backend/access/transam",
"backend/bootstrap",
"backend/catalog",
"backend/commands",
"backend/executor",
"backend/foreign",
"backend/jit/llvm",
"backend/lib",
"backend/libpq",
"backend/main",
"backend/nodes",
"backend/optimizer/geqo",
"backend/optimizer/path",
"backend/optimizer/plan",
"backend/optimizer/prep",
"backend/optimizer/util",
"backend/parser",
"backend/partitioning",
"backend/port",
"backend/postmaster",
"backend/regex",
"backend/replication",
"backend/rewrite",
"backend/statistics",
"backend/storage/buffer",
"backend/storage/file",
"backend/storage/freespace",
"backend/storage/ipc",
"backend/storage/large_object",
"backend/storage/lmgr",
"backend/storage/page",
"backend/storage/smgr",
"backend/storage/sync",
"backend/tcop",
"backend/tsearch",
"backend/utils/adt",
"backend/utils/cache",
"backend/utils/error",
"backend/utils/fmgr",
"backend/utils/hash",
"backend/utils/init",
"backend/utils/mb",
"backend/utils/misc",
"backend/utils/mmgr",
"backend/utils/resowner",
"backend/utils/sort",
"backend/utils/time",
];
let mut files = Vec::new();
for dir_name in &dirs {
let dir_path = src_dir.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("FRONTEND".into(), None),
("PGXC".into(), None),
("PGXC_FREE".into(), None),
("XLOG_BLCKSZ".into(), Some(self.wal_blocksize.to_string())),
("BLCKSZ".into(), Some(self.blocksize.to_string())),
(
"RELSEG_SIZE".into(),
Some(format!(
"{}",
self.segsize * 1024 * 1024 * 1024 / self.blocksize
)),
),
(
"WAL_SEGMENT_SIZE".into(),
Some(format!("{}", self.wal_segsize * 1024 * 1024)),
),
(
"HAVE_LIBZ".into(),
if self.with_zlib {
Some("1".into())
} else {
None
},
),
];
if self.with_icu {
defs.push(("USE_ICU".into(), Some("1".into())));
}
if self.with_ssl {
defs.push(("USE_OPENSSL".into(), Some("1".into())));
}
if self.with_llvm {
defs.push(("USE_LLVM".into(), Some("1".into())));
defs.push(("LLVM_MAJOR".into(), Some("18".into())));
}
if self.enable_debug {
defs.push(("USE_DEBUG".into(), Some("1".into())));
}
if self.enable_cassert {
defs.push(("USE_ASSERT_CHECKING".into(), Some("1".into())));
}
if self.enable_nls {
defs.push(("ENABLE_NLS".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.join("src").join("include"),
root.join("src").join("backend"),
root.join("src").join("interfaces").join("libpq"),
root.join("src").join("port"),
];
if let Some(ref icu) = self.icu_include {
dirs.push(icu.clone());
}
if let Some(ref ssl) = self.ssl_include {
dirs.push(ssl.clone());
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c17".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-Wno-sign-compare".into(),
"-Wno-missing-field-initializers".into(),
"-fno-strict-aliasing".into(),
"-fwrapv".into(),
"-fexcess-precision=standard".into(),
];
if self.with_llvm {
flags.push("-fPIC".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(), "-lpthread".into()];
if self.with_ssl {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
if self.with_icu {
flags.push("-licuuc".into());
flags.push("-licui18n".into());
}
if self.with_zlib {
flags.push("-lz".into());
}
if self.with_readline {
flags.push("-lreadline".into());
}
if self.with_libxml {
flags.push("-lxml2".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into(), "pthread".into()];
if self.with_ssl {
libs.extend(vec!["ssl".into(), "crypto".into()]);
}
if self.with_icu {
libs.extend(vec!["icuuc".into(), "icui18n".into()]);
}
if self.with_zlib {
libs.push("z".into());
}
if self.with_readline {
libs.push("readline".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 defs = self.defines();
let flags = self.compiler_flags();
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
let defines_str: String = 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(" "),
defines_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("postgres").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
let mut notes = vec![
format!("Block size: {} bytes", self.blocksize),
format!("WAL segment size: {} MB", self.wal_segsize),
];
if self.with_llvm {
notes.push("LLVM JIT enabled for expression evaluation".into());
}
if self.with_ssl {
notes.push("OpenSSL support enabled".into());
}
notes
}
}
pub struct X86MySQLConfig {
pub storage_engines: Vec<String>,
pub boost_path: Option<PathBuf>,
pub with_boost: bool,
pub with_innodb: bool,
pub with_myisam: bool,
pub with_archive: bool,
pub with_blackhole: bool,
pub with_csv: bool,
pub with_federated: bool,
pub with_ndbcluster: bool,
pub with_perfschema: bool,
pub with_ssl: bool,
pub ssl_library: X86MysqlSslType,
pub default_charset: String,
pub default_collation: String,
pub enable_largefile: bool,
pub with_debug: bool,
pub simd_level: X86SIMDLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MysqlSslType {
OpenSSL,
WolfSSL,
None,
}
impl Default for X86MySQLConfig {
fn default() -> Self {
Self {
storage_engines: vec![
"InnoDB".into(),
"MyISAM".into(),
"CSV".into(),
"Archive".into(),
"Blackhole".into(),
"Federated".into(),
"MRG_MYISAM".into(),
"MEMORY".into(),
"PERFORMANCE_SCHEMA".into(),
],
boost_path: None,
with_boost: true,
with_innodb: true,
with_myisam: true,
with_archive: true,
with_blackhole: true,
with_csv: true,
with_federated: false,
with_ndbcluster: false,
with_perfschema: true,
with_ssl: true,
ssl_library: X86MysqlSslType::OpenSSL,
default_charset: "utf8mb4".into(),
default_collation: "utf8mb4_0900_ai_ci".into(),
enable_largefile: true,
with_debug: false,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86MySQLConfig {
fn name(&self) -> &str {
"mysql"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let search_dirs = [
"sql",
"storage",
"mysys",
"strings",
"vio",
"client",
"sql-common",
"libmysql",
"libservices",
"plugin",
];
for dir_name in &search_dirs {
let dir_path = root.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cc" || ext == "c" {
files.push(path);
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("MYSQL_SERVER".into(), None),
("HAVE_CONFIG_H".into(), None),
(
"DBUG_OFF".into(),
if !self.with_debug {
Some("1".into())
} else {
None
},
),
("UNIV_LINUX".into(), None),
("HAVE_LARGE_PAGES".into(), Some("1".into())),
("LINUX_LARGE_PAGES".into(), Some("1".into())),
];
if self.with_innodb {
defs.push(("WITH_INNODB".into(), Some("1".into())));
}
if self.with_ssl {
defs.push(("HAVE_OPENSSL".into(), Some("1".into())));
}
if self.enable_largefile {
defs.push(("_LARGEFILE_SOURCE".into(), None));
defs.push(("_LARGEFILE64_SOURCE".into(), None));
defs.push(("_FILE_OFFSET_BITS".into(), Some("64".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.join("include"),
root.join("sql"),
root.join("storage").join("innobase").join("include"),
];
if let Some(ref bp) = self.boost_path {
dirs.push(bp.clone());
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++17".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-Wno-sign-compare".into(),
"-fno-strict-aliasing".into(),
"-fno-omit-frame-pointer".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(),
"-lpthread".into(),
"-ldl".into(),
"-lrt".into(),
];
if self.with_ssl {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into(), "pthread".into(), "dl".into(), "rt".into()];
if self.with_ssl {
libs.extend(vec!["ssl".into(), "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 defs = self.defines();
let flags = self.compiler_flags();
for src in &sources {
let stem = src.file_stem().unwrap_or_default().to_string_lossy();
let obj = output_dir.join(format!("{}.o", stem));
let defines_str: String = 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!(
"c++ {} {} -c {} -o {}",
flags.join(" "),
defines_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("mysqld").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![
format!("Storage engines: {}", self.storage_engines.join(", ")),
format!(
"Charset: {} Collation: {}",
self.default_charset, self.default_collation
),
]
}
}
pub struct X86MongoDBConfig {
pub with_wiredtiger: bool,
pub with_ssl: bool,
pub use_system_libs: bool,
pub asio_mode: String,
pub build_shell: bool,
pub build_tools: bool,
pub target_arch: String,
pub enable_debug: bool,
pub simd_level: X86SIMDLevel,
pub link_model: X86MongoLinkModel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MongoLinkModel {
Dynamic,
Static,
Auto,
}
impl Default for X86MongoDBConfig {
fn default() -> Self {
Self {
with_wiredtiger: true,
with_ssl: true,
use_system_libs: false,
asio_mode: "async".into(),
build_shell: true,
build_tools: true,
target_arch: "x86_64".into(),
enable_debug: false,
simd_level: X86SIMDLevel::SSE42,
link_model: X86MongoLinkModel::Auto,
}
}
}
impl X86ProjectConfigurable for X86MongoDBConfig {
fn name(&self) -> &str {
"mongodb"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src").join("mongo");
let mut files = Vec::new();
let search_dirs = [
"db",
"s",
"client",
"shell",
"util",
"transport",
"executor",
"watchdog",
"platform",
"bson",
"idhack",
];
for dir_name in &search_dirs {
let dir_path = src_dir.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cpp" || ext == "c" {
files.push(path);
}
}
}
}
}
}
if self.with_wiredtiger {
let wt_dir = root.join("src").join("third_party").join("wiredtiger");
if wt_dir.exists() {
if let Ok(entries) = fs::read_dir(&wt_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() || path.extension().map_or(false, |e| e == "c") {
if path.is_file() {
files.push(path);
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("MONGO_CONFIG_HEADER".into(), None),
("MONGO_HAVE_HEADER_UNISTD_H".into(), None),
("_CONSOLE".into(), None),
("_UNICODE".into(), None),
("ABSL_ALLOCATOR_NOTHROW".into(), Some("1".into())),
];
if self.with_ssl {
defs.push(("MONGO_SSL".into(), Some("1".into())));
}
if self.with_wiredtiger {
defs.push(("WIREDTIGER_ENABLED".into(), Some("1".into())));
}
if self.enable_debug {
defs.push(("MONGO_DEBUG_BUILD".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.join("src").join("mongo"),
root.join("src").join("third_party"),
root.join("build").join("opt").join("mongo"),
]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++20".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(), "-lpthread".into(), "-ldl".into()];
if self.with_ssl {
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.with_ssl {
libs.extend(vec!["ssl".into(), "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 defs = self.defines();
let flags = self.compiler_flags();
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 {}",
flags.join(" "),
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("mongod").display())]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::SCons
}
fn notes(&self) -> Vec<String> {
vec![
format!("WiredTiger: {}", self.with_wiredtiger),
format!("SSL: {}", self.with_ssl),
"MongoDB uses SCons with custom python build scripts".into(),
]
}
}
pub struct X86RedisExtraConfig {
pub malloc: X86ExtraMallocType,
pub with_lua: bool,
pub lua_version: String,
pub with_jemalloc: bool,
pub with_tls: bool,
pub with_systemd: bool,
pub optimize_for_size: bool,
pub enable_lto: bool,
pub simd_level: X86SIMDLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ExtraMallocType {
Jemalloc,
Libc,
Tcmalloc,
}
impl Default for X86RedisExtraConfig {
fn default() -> Self {
Self {
malloc: X86ExtraMallocType::Jemalloc,
with_lua: true,
lua_version: "5.1".into(),
with_jemalloc: true,
with_tls: false,
with_systemd: false,
optimize_for_size: false,
enable_lto: true,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86RedisExtraConfig {
fn name(&self) -> &str {
"redis-extra"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src");
[
"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",
"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",
"release.c",
"replication.c",
"resp_parser.c",
"rio.c",
"script.c",
"script_lua.c",
"sds.c",
"sentinel.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",
"redis-benchmark.c",
"redis-check-aof.c",
"redis-check-rdb.c",
"redis-cli.c",
]
.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.with_jemalloc {
defs.push(("USE_JEMALLOC".into(), Some("1".into())));
}
if self.with_tls {
defs.push(("USE_OPENSSL".into(), Some("1".into())));
}
if self.with_lua {
defs.push(("USE_LUA".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![root.join("src")];
if self.with_jemalloc {
dirs.push(root.join("deps").join("jemalloc").join("include"));
}
if self.with_lua {
dirs.push(root.join("deps").join("lua").join("src"));
}
dirs.push(root.join("deps").join("hiredis"));
dirs.push(root.join("deps").join("linenoise"));
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-sign-compare".into(),
"-Wno-unused-parameter".into(),
];
if self.optimize_for_size {
flags.push("-Os".into());
}
if self.enable_lto {
flags.push("-flto".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(), "-lpthread".into(), "-ldl".into()];
if self.with_tls {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
if self.enable_lto {
flags.push("-flto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into(), "pthread".into(), "dl".into()];
if self.with_tls {
libs.extend(vec!["ssl".into(), "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),
format!("Lua: {} (version {})", self.with_lua, self.lua_version),
]
}
}
pub struct X86MemcachedConfig {
pub libevent_include: Option<PathBuf>,
pub libevent_lib: Option<PathBuf>,
pub enable_sasl: bool,
pub enable_tls: bool,
pub enable_dtrace: bool,
pub enable_experimental: bool,
pub max_item_size: String,
pub memory_limit: Option<String>,
pub port: u16,
pub threads: u32,
pub max_connections: u32,
}
impl Default for X86MemcachedConfig {
fn default() -> Self {
Self {
libevent_include: None,
libevent_lib: None,
enable_sasl: false,
enable_tls: false,
enable_dtrace: false,
enable_experimental: false,
max_item_size: "1m".into(),
memory_limit: None,
port: 11211,
threads: 4,
max_connections: 1024,
}
}
}
impl X86ProjectConfigurable for X86MemcachedConfig {
fn name(&self) -> &str {
"memcached"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let sources = [
"memcached.c",
"hash.c",
"slabs.c",
"items.c",
"assoc.c",
"thread.c",
"stats.c",
"util.c",
"cache.c",
"extstore.c",
"crawler.c",
"logger.c",
"restart.c",
"sasl_defs.c",
"proto_text.c",
"proto_bin.c",
"bipbuffer.c",
];
sources
.iter()
.map(|s| root.join(s))
.filter(|p| p.exists())
.collect()
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![("HAVE_CONFIG_H".into(), None)];
if self.enable_sasl {
defs.push(("ENABLE_SASL".into(), Some("1".into())));
}
if self.enable_tls {
defs.push(("TLS".into(), Some("1".into())));
}
if self.enable_experimental {
defs.push(("EXTSTORE".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![root.to_path_buf()];
if let Some(ref inc) = self.libevent_include {
dirs.push(inc.clone());
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c11".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-pthread".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec!["-levent".into(), "-lpthread".into(), "-lm".into()];
if self.enable_sasl {
flags.push("-lsasl2".into());
}
if self.enable_tls {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["event".into(), "pthread".into(), "m".into()];
if self.enable_sasl {
libs.push("sasl2".into());
}
if self.enable_tls {
libs.extend(vec!["ssl".into(), "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("memcached").display()
)]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
vec![
format!("Port: {} Threads: {}", self.port, self.threads),
"memcached requires libevent for event-driven networking".into(),
]
}
}
pub struct X86NginxExtraConfig {
pub with_http: bool,
pub with_http_ssl: bool,
pub with_http_v2: bool,
pub with_http_v3: bool,
pub with_http_gzip: bool,
pub with_http_stub_status: bool,
pub with_http_realip: bool,
pub with_http_addition: bool,
pub with_http_sub: bool,
pub with_http_dav: bool,
pub with_http_flv: bool,
pub with_http_mp4: bool,
pub with_http_gunzip: bool,
pub with_http_gzip_static: bool,
pub with_http_random_index: bool,
pub with_http_secure_link: bool,
pub with_http_degradation: bool,
pub with_http_slice: bool,
pub with_http_perl: bool,
pub with_http_auth_request: bool,
pub with_http_upstream_hash: bool,
pub with_http_upstream_ip_hash: bool,
pub with_http_upstream_least_conn: bool,
pub with_http_upstream_random: bool,
pub with_http_upstream_keepalive: bool,
pub with_http_upstream_zone: bool,
pub with_stream: bool,
pub with_stream_ssl: bool,
pub with_stream_realip: bool,
pub with_stream_geoip: bool,
pub with_mail: bool,
pub with_mail_ssl: bool,
pub with_threads: bool,
pub with_file_aio: bool,
pub with_ipv6: bool,
pub with_debug: bool,
pub with_pcre: bool,
pub with_zlib: bool,
pub openssl_path: Option<PathBuf>,
pub simd_level: X86SIMDLevel,
}
impl Default for X86NginxExtraConfig {
fn default() -> Self {
Self {
with_http: true,
with_http_ssl: true,
with_http_v2: true,
with_http_v3: false,
with_http_gzip: true,
with_http_stub_status: true,
with_http_realip: true,
with_http_addition: false,
with_http_sub: true,
with_http_dav: false,
with_http_flv: false,
with_http_mp4: false,
with_http_gunzip: false,
with_http_gzip_static: true,
with_http_random_index: false,
with_http_secure_link: false,
with_http_degradation: false,
with_http_slice: false,
with_http_perl: false,
with_http_auth_request: true,
with_http_upstream_hash: true,
with_http_upstream_ip_hash: true,
with_http_upstream_least_conn: true,
with_http_upstream_random: true,
with_http_upstream_keepalive: true,
with_http_upstream_zone: true,
with_stream: true,
with_stream_ssl: true,
with_stream_realip: false,
with_stream_geoip: false,
with_mail: false,
with_mail_ssl: false,
with_threads: true,
with_file_aio: true,
with_ipv6: true,
with_debug: false,
with_pcre: true,
with_zlib: true,
openssl_path: None,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86NginxExtraConfig {
fn name(&self) -> &str {
"nginx-extra"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let core_dir = root.join("src").join("core");
for f in &[
"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",
] {
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);
}
}
if self.with_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);
}
}
}
if self.with_stream {
let stream_dir = root.join("src").join("stream");
for f in &["ngx_stream.c", "ngx_stream_core_module.c"] {
let p = stream_dir.join(f);
if p.exists() {
files.push(p);
}
}
}
if self.with_mail {
let mail_dir = root.join("src").join("mail");
for f in &["ngx_mail.c", "ngx_mail_core_module.c"] {
let p = mail_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.with_http {
defs.push(("NGX_HTTP".into(), Some("1".into())));
}
if self.with_http_v2 {
defs.push(("NGX_HTTP_V2".into(), Some("1".into())));
}
if self.with_http_v3 {
defs.push(("NGX_HTTP_V3".into(), Some("1".into())));
}
if self.with_stream {
defs.push(("NGX_STREAM".into(), Some("1".into())));
}
if self.with_mail {
defs.push(("NGX_MAIL".into(), Some("1".into())));
}
if self.with_file_aio {
defs.push(("NGX_HAVE_FILE_AIO".into(), Some("1".into())));
}
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("src").join("stream"),
root.join("src").join("mail"),
root.join("objs"),
];
if let Some(ref ossl) = self.openssl_path {
dirs.push(ossl.join("include"));
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-pipe".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.with_http_ssl || self.with_stream_ssl || self.with_mail_ssl {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
if self.with_pcre {
flags.push("-lpcre".into());
}
if self.with_zlib {
flags.push("-lz".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = Vec::new();
if self.with_http_ssl {
libs.extend(vec!["ssl".into(), "crypto".into()]);
}
if self.with_pcre {
libs.push("pcre".into());
}
if self.with_zlib {
libs.push("z".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(),
};
for src in &self.sources(root) {
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()),
format!("{} -V", build_dir.join("nginx").display()),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
let mut notes = vec!["nginx extended configuration".into()];
if self.with_http_v2 {
notes.push("HTTP/2 enabled".into());
}
if self.with_stream {
notes.push("TCP/UDP stream proxy enabled".into());
}
if self.with_mail {
notes.push("Mail proxy enabled".into());
}
notes
}
}
pub struct X86ApacheHttpdConfig {
pub mpm: X86ApacheMpm,
pub modules: Vec<String>,
pub enable_static: bool,
pub enable_shared: bool,
pub with_ssl: bool,
pub ssl_path: Option<PathBuf>,
pub with_http2: bool,
pub with_proxy: bool,
pub with_rewrite: bool,
pub with_include: bool,
pub with_cgi: bool,
pub with_cgid: bool,
pub with_deflate: bool,
pub with_expires: bool,
pub with_headers: bool,
pub with_status: bool,
pub with_info: bool,
pub with_alias: bool,
pub with_dir: bool,
pub with_auth_basic: bool,
pub with_log_config: bool,
pub with_mime: bool,
pub with_setenvif: bool,
pub server_root: Option<PathBuf>,
pub port: u16,
pub user: String,
pub group: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ApacheMpm {
Event,
Worker,
Prefork,
Simple,
Motorz,
}
impl X86ApacheMpm {
pub fn as_str(&self) -> &'static str {
match self {
Self::Event => "event",
Self::Worker => "worker",
Self::Prefork => "prefork",
Self::Simple => "simple",
Self::Motorz => "motorz",
}
}
}
impl Default for X86ApacheHttpdConfig {
fn default() -> Self {
Self {
mpm: X86ApacheMpm::Event,
modules: vec![
"access_compat".into(),
"actions".into(),
"alias".into(),
"allowmethods".into(),
"auth_basic".into(),
"authn_core".into(),
"authn_file".into(),
"authz_core".into(),
"authz_host".into(),
"authz_user".into(),
"autoindex".into(),
"cgi".into(),
"deflate".into(),
"dir".into(),
"env".into(),
"expires".into(),
"headers".into(),
"http2".into(),
"include".into(),
"info".into(),
"log_config".into(),
"logio".into(),
"mime".into(),
"negotiation".into(),
"proxy".into(),
"proxy_http".into(),
"rewrite".into(),
"setenvif".into(),
"socache_shmcb".into(),
"ssl".into(),
"status".into(),
"unique_id".into(),
"unixd".into(),
"version".into(),
"vhost_alias".into(),
],
enable_static: false,
enable_shared: true,
with_ssl: true,
ssl_path: None,
with_http2: true,
with_proxy: true,
with_rewrite: true,
with_include: false,
with_cgi: true,
with_cgid: false,
with_deflate: true,
with_expires: true,
with_headers: true,
with_status: true,
with_info: false,
with_alias: true,
with_dir: true,
with_auth_basic: true,
with_log_config: true,
with_mime: true,
with_setenvif: true,
server_root: None,
port: 80,
user: "www-data".into(),
group: "www-data".into(),
}
}
}
impl X86ProjectConfigurable for X86ApacheHttpdConfig {
fn name(&self) -> &str {
"httpd"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let search_dirs = [
"server",
"modules/aaa",
"modules/arch/win32",
"modules/cache",
"modules/core",
"modules/database",
"modules/debugging",
"modules/filters",
"modules/http",
"modules/loggers",
"modules/metadata",
"modules/proxy",
"modules/session",
"modules/slotmem",
"modules/ssl",
"modules/test",
];
for dir_name in &search_dirs {
let dir_path = root.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("AP_SERVER_BASEREVISION".into(), Some(r#""2.4.59""#.into())),
("LINUX".into(), Some("2".into())),
("_REENTRANT".into(), None),
("_LARGEFILE64_SOURCE".into(), None),
];
if self.with_ssl {
defs.push(("HAVE_OPENSSL".into(), Some("1".into())));
}
if self.with_http2 {
defs.push(("HAVE_NGHTTP2".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.join("include"),
root.join("modules/aaa"),
root.join("modules/http"),
root.join("server"),
root.join("os/unix"),
];
if let Some(ref ssl) = self.ssl_path {
dirs.push(ssl.join("include"));
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
vec![
"-std=c11".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-pthread".into(),
]
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec!["-lpthread".into(), "-lm".into()];
if self.with_ssl {
flags.push("-lssl".into());
flags.push("-lcrypto".into());
}
if self.with_http2 {
flags.push("-lnghttp2".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["pthread".into(), "m".into()];
if self.with_ssl {
libs.extend(vec!["ssl".into(), "crypto".into()]);
}
if self.with_http2 {
libs.push("nghttp2".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(),
};
for src in &self.sources(root) {
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("httpd").display()),
format!("{} -V", build_dir.join("httpd").display()),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
vec![
format!("MPM: {}", self.mpm.as_str()),
format!(
"Modules: {} ({} total)",
self.modules.len(),
self.modules.len()
),
format!("User: {} Group: {}", self.user, self.group),
]
}
}
pub struct X86NodeJsConfig {
pub v8_path: Option<PathBuf>,
pub openssl_path: Option<PathBuf>,
pub zlib_path: Option<PathBuf>,
pub libuv_path: Option<PathBuf>,
pub nghttp2_path: Option<PathBuf>,
pub icu_path: Option<PathBuf>,
pub use_bundled_deps: bool,
pub with_inspector: bool,
pub with_intl: bool,
pub intl_mode: X86NodeIntlMode,
pub debug: bool,
pub enable_lto: bool,
pub enable_pgo: bool,
pub with_dtrace: bool,
pub with_etw: bool,
pub cross_compiling: Option<String>,
pub simd_level: X86SIMDLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86NodeIntlMode {
None,
SmallIcu,
FullIcu,
SystemIcu,
}
impl Default for X86NodeJsConfig {
fn default() -> Self {
Self {
v8_path: None,
openssl_path: None,
zlib_path: None,
libuv_path: None,
nghttp2_path: None,
icu_path: None,
use_bundled_deps: true,
with_inspector: true,
with_intl: true,
intl_mode: X86NodeIntlMode::FullIcu,
debug: false,
enable_lto: false,
enable_pgo: false,
with_dtrace: false,
with_etw: false,
cross_compiling: None,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86NodeJsConfig {
fn name(&self) -> &str {
"node"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src");
let mut files = Vec::new();
if src_dir.exists() {
if let Ok(entries) = fs::read_dir(&src_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cc" || ext == "c" {
files.push(path);
}
}
}
}
}
if self.use_bundled_deps {
let deps_dir = root.join("deps");
if deps_dir.exists() {
for dep in &["uv", "zlib", "cares", "v8"] {
let dep_dir = deps_dir.join(dep);
if dep_dir.exists() {
if let Ok(entries) = fs::read_dir(&dep_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cc" || ext == "c" {
files.push(path);
}
}
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("NODE_ARCH".into(), Some(r#""x64""#.into())),
("NODE_PLATFORM".into(), Some(r#""linux""#.into())),
("NODE_WANT_INTERNALS".into(), Some("1".into())),
("ARCH_IS_64_BIT".into(), Some("1".into())),
];
if self.debug {
defs.push(("DEBUG".into(), Some("1".into())));
}
if self.with_intl {
defs.push(("NODE_HAVE_I18N_SUPPORT".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.join("src"),
root.join("deps").join("v8").join("include"),
];
if let Some(ref v8) = self.v8_path {
dirs.push(v8.join("include"));
}
if let Some(ref ossl) = self.openssl_path {
dirs.push(ossl.join("include"));
}
if let Some(ref zlib) = self.zlib_path {
dirs.push(zlib.clone());
}
if let Some(ref uv) = self.libuv_path {
dirs.push(uv.join("include"));
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++20".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-fno-rtti".into(),
"-pthread".into(),
];
if self.enable_lto {
flags.push("-flto".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!["-lpthread".into(), "-ldl".into(), "-lm".into()];
flags.push("-lssl".into());
flags.push("-lcrypto".into());
flags.push("-lz".into());
if self.enable_lto {
flags.push("-flto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
vec![
"pthread".into(),
"dl".into(),
"m".into(),
"ssl".into(),
"crypto".into(),
"z".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(),
};
for src in &self.sources(root) {
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!("{} --version", build_dir.join("node").display()),
format!(
"{} -e 'console.log(\"ok\")'",
build_dir.join("node").display()
),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
let mut notes = vec!["Node.js runtime with V8 engine".into()];
if self.use_bundled_deps {
notes.push("Using bundled dependencies".into());
}
if self.enable_lto {
notes.push("LTO enabled".into());
}
if self.debug {
notes.push("Debug build".into());
}
notes
}
}
pub struct X86CPythonConfig {
pub extensions: Vec<String>,
pub disabled_extensions: Vec<String>,
pub with_lto: bool,
pub with_pgo: bool,
pub with_computed_gotos: bool,
pub with_assertions: bool,
pub with_debug: bool,
pub with_valgrind: bool,
pub with_dtrace: bool,
pub opt_flags: String,
pub enable_loadable_sqlite_extensions: bool,
pub enable_big_digits: bool,
pub enable_ipv6: bool,
pub simd_level: X86SIMDLevel,
}
impl Default for X86CPythonConfig {
fn default() -> Self {
Self {
extensions: vec![
"_socket".into(),
"_ssl".into(),
"_json".into(),
"_csv".into(),
"_sqlite3".into(),
"_bz2".into(),
"_lzma".into(),
"_ctypes".into(),
"_curses".into(),
"_decimal".into(),
"_elementtree".into(),
"_hashlib".into(),
"_multiprocessing".into(),
"_posixsubprocess".into(),
"_random".into(),
"_struct".into(),
"_testcapi".into(),
"array".into(),
"binascii".into(),
"cmath".into(),
"fcntl".into(),
"math".into(),
"mmap".into(),
"parser".into(),
"readline".into(),
"resource".into(),
"select".into(),
"syslog".into(),
"termios".into(),
"unicodedata".into(),
"zlib".into(),
],
disabled_extensions: vec!["_tkinter".into(), "_gdbm".into()],
with_lto: true,
with_pgo: false,
with_computed_gotos: true,
with_assertions: false,
with_debug: false,
with_valgrind: false,
with_dtrace: false,
opt_flags: "-O3".into(),
enable_loadable_sqlite_extensions: true,
enable_big_digits: false,
enable_ipv6: true,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86CPythonConfig {
fn name(&self) -> &str {
"cpython"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let core_dir = root.join("Python");
if core_dir.exists() {
for name in &[
"python.c",
"bltinmodule.c",
"ceval.c",
"codecs.c",
"compile.c",
"errors.c",
"getopt.c",
"getplatform.c",
"import.c",
"marshal.c",
"modsupport.c",
"mysnprintf.c",
"pyarena.c",
"pyctype.c",
"pyfpe.c",
"pyhash.c",
"pylifecycle.c",
"pymath.c",
"pystate.c",
"pystrcmp.c",
"pystrhex.c",
"pystrtod.c",
"pythonrun.c",
"random.c",
"structmember.c",
"symtable.c",
"sysmodule.c",
"traceback.c",
"wordcode_helpers.c",
] {
let p = core_dir.join(name);
if p.exists() {
files.push(p);
}
}
}
let obj_dir = root.join("Objects");
if obj_dir.exists() {
for name in &[
"abstract.c",
"accu.c",
"boolobject.c",
"bytearrayobject.c",
"bytesobject.c",
"cellobject.c",
"classobject.c",
"codeobject.c",
"complexobject.c",
"descrobject.c",
"dictobject.c",
"enumobject.c",
"exceptions.c",
"floatobject.c",
"frameobject.c",
"funcobject.c",
"interpreteridobject.c",
"iterobject.c",
"listobject.c",
"longobject.c",
"memoryobject.c",
"methodobject.c",
"moduleobject.c",
"namespaceobject.c",
"object.c",
"obmalloc.c",
"odictobject.c",
"picklebufobject.c",
"rangeobject.c",
"setobject.c",
"sliceobject.c",
"stringobject.c",
"structseq.c",
"tupleobject.c",
"typeobject.c",
"unicodeobject.c",
"unicodectype.c",
"weakrefobject.c",
] {
let p = obj_dir.join(name);
if p.exists() {
files.push(p);
}
}
}
let mod_dir = root.join("Modules");
if mod_dir.exists() {
if let Ok(entries) = fs::read_dir(&mod_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("Py_BUILD_CORE".into(), None),
("_GNU_SOURCE".into(), None),
("_FILE_OFFSET_BITS".into(), Some("64".into())),
];
if self.with_computed_gotos {
defs.push(("USE_COMPUTED_GOTOS".into(), Some("1".into())));
}
if self.enable_ipv6 {
defs.push(("ENABLE_IPV6".into(), Some("1".into())));
}
if self.with_assertions {
defs.push(("Py_DEBUG".into(), None));
}
if self.enable_big_digits {
defs.push(("PYLONG_BITS_IN_DIGIT".into(), Some("30".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.join("Include"),
root.join("Include").join("internal"),
root.join("Python"),
root.join("Modules"),
]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".into(),
self.opt_flags.clone(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-Wno-missing-field-initializers".into(),
"-Wno-strict-prototypes".into(),
"-fno-strict-aliasing".into(),
"-fwrapv".into(),
];
if self.with_lto {
flags.push("-flto".into());
}
if self.with_pgo {
flags.push("-fprofile-generate".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(),
"-lpthread".into(),
"-ldl".into(),
"-lutil".into(),
];
flags.push("-lssl".into());
flags.push("-lcrypto".into());
flags.push("-lz".into());
flags.push("-lbz2".into());
flags.push("-llzma".into());
if self.with_lto {
flags.push("-flto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
vec![
"m".into(),
"pthread".into(),
"dl".into(),
"util".into(),
"ssl".into(),
"crypto".into(),
"z".into(),
"bz2".into(),
"lzma".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(),
};
for src in &self.sources(root) {
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("python").display()),
format!(
"{} -c 'print(\"hello\")'",
build_dir.join("python").display()
),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
let mut notes = vec!["CPython interpreter".into()];
if self.with_lto {
notes.push("LTO enabled".into());
}
if self.with_pgo {
notes.push("PGO enabled".into());
}
if self.with_computed_gotos {
notes.push("Computed gotos for fast dispatch".into());
}
notes
}
}
pub struct X86RubyConfig {
pub with_yjit: bool,
pub yjit_support: bool,
pub with_rjit: bool,
pub with_mjit: bool,
pub enable_debug: bool,
pub enable_shared: bool,
pub enable_dtrace: bool,
pub with_gmp: bool,
pub with_jemalloc: bool,
pub opt_flags: String,
pub yjit_stats: bool,
pub ruby_version: String,
}
impl Default for X86RubyConfig {
fn default() -> Self {
Self {
with_yjit: true,
yjit_support: true,
with_rjit: false,
with_mjit: false,
enable_debug: false,
enable_shared: true,
enable_dtrace: false,
with_gmp: false,
with_jemalloc: true,
opt_flags: "-O3".into(),
yjit_stats: false,
ruby_version: "3.3.0".into(),
}
}
}
impl X86ProjectConfigurable for X86RubyConfig {
fn name(&self) -> &str {
"ruby"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
for dir_name in &["", "enc", "enc/trans", "enc/unicode", "ext"] {
let dir_path = root.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
}
if self.with_yjit {
let yjit_dir = root.join("yjit");
if yjit_dir.exists() {
if let Ok(entries) = fs::read_dir(&yjit_dir.join("src")) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "rs" || ext == "c" {
files.push(path);
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
(
"RUBY_VERSION".into(),
Some(format!(r#""{}""#, self.ruby_version)),
),
("RUBY_PLATFORM".into(), Some(r#""x86_64-linux""#.into())),
("_GNU_SOURCE".into(), None),
];
if self.with_yjit {
defs.push(("YJIT_SUPPORT".into(), Some("1".into())));
}
if self.with_rjit {
defs.push(("RJIT_SUPPORT".into(), Some("1".into())));
}
if self.with_mjit {
defs.push(("MJIT_SUPPORT".into(), Some("1".into())));
}
if self.yjit_stats {
defs.push(("YJIT_STATS".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.to_path_buf(),
root.join("include"),
root.join("enc"),
root.join("ext").join("include"),
];
if self.with_yjit {
dirs.push(root.join("yjit").join("src"));
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".into(),
self.opt_flags.clone(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-Wno-missing-field-initializers".into(),
"-fno-strict-aliasing".into(),
"-fwrapv".into(),
];
if self.with_yjit {
flags.push("-DYJIT_SUPPORT".into());
}
flags
}
fn linker_flags(&self) -> Vec<String> {
let mut flags = vec![
"-lm".into(),
"-lpthread".into(),
"-ldl".into(),
"-lcrypt".into(),
];
if self.with_gmp {
flags.push("-lgmp".into());
}
if self.with_jemalloc {
flags.push("-ljemalloc".into());
}
if self.with_yjit {
flags.push("-lstdc++".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into(), "pthread".into(), "dl".into(), "crypt".into()];
if self.with_gmp {
libs.push("gmp".into());
}
if self.with_jemalloc {
libs.push("jemalloc".into());
}
if self.with_yjit {
libs.push("stdc++".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(),
};
for src in &self.sources(root) {
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("ruby").display()),
format!("{} -e 'puts \"hello\"'", build_dir.join("ruby").display()),
format!(
"{} --yjit -e 'puts RUBY_DESCRIPTION'",
build_dir.join("ruby").display()
),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
let mut notes = vec![format!("Ruby {} with YJIT", self.ruby_version)];
if self.with_yjit {
notes.push("YJIT: Yet Another Ruby JIT compiler (Rust backend)".into());
}
if self.with_rjit {
notes.push("RJIT: Lightweight pure-Ruby JIT".into());
}
notes
}
}
pub struct X86PhpConfig {
pub extensions: Vec<String>,
pub zend_extensions: Vec<String>,
pub with_zts: bool,
pub with_debug: bool,
pub with_opcache: bool,
pub with_jit: bool,
pub jit_buffer_size: u64,
pub with_fpm: bool,
pub with_cli: bool,
pub with_cgi: bool,
pub with_embed: bool,
pub with_mysqli: bool,
pub with_pdo: bool,
pub with_pdo_mysql: bool,
pub with_sqlite3: bool,
pub with_openssl: bool,
pub with_zlib: bool,
pub with_libxml: bool,
pub with_mbstring: bool,
pub with_curl: bool,
pub with_gd: bool,
pub with_iconv: bool,
pub with_json: bool,
pub with_readline: bool,
pub opt_flags: String,
pub simd_level: X86SIMDLevel,
}
impl Default for X86PhpConfig {
fn default() -> Self {
Self {
extensions: vec![
"bcmath".into(),
"calendar".into(),
"ctype".into(),
"curl".into(),
"dom".into(),
"fileinfo".into(),
"filter".into(),
"gd".into(),
"hash".into(),
"iconv".into(),
"json".into(),
"mbstring".into(),
"mysqli".into(),
"opcache".into(),
"openssl".into(),
"pcre".into(),
"pdo".into(),
"pdo_mysql".into(),
"pdo_sqlite".into(),
"phar".into(),
"posix".into(),
"readline".into(),
"session".into(),
"simplexml".into(),
"sockets".into(),
"sodium".into(),
"sqlite3".into(),
"tokenizer".into(),
"xml".into(),
"xmlreader".into(),
"xmlwriter".into(),
"zip".into(),
"zlib".into(),
],
zend_extensions: vec!["opcache".into()],
with_zts: false,
with_debug: false,
with_opcache: true,
with_jit: true,
jit_buffer_size: 0,
with_fpm: true,
with_cli: true,
with_cgi: false,
with_embed: false,
with_mysqli: true,
with_pdo: true,
with_pdo_mysql: true,
with_sqlite3: true,
with_openssl: true,
with_zlib: true,
with_libxml: true,
with_mbstring: true,
with_curl: true,
with_gd: false,
with_iconv: true,
with_json: true,
with_readline: true,
opt_flags: "-O2".into(),
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86PhpConfig {
fn name(&self) -> &str {
"php"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let zend_dir = root.join("Zend");
if zend_dir.exists() {
if let Ok(entries) = fs::read_dir(&zend_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
for dir_name in &["main", "ext", "sapi/cli", "sapi/fpm", "sapi/cgi"] {
let dir_path = root.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
continue;
}
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
if dir_name == "ext" {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let subdir = entry.path();
if subdir.is_dir() {
if let Ok(sub_entries) = fs::read_dir(&subdir) {
for sub_entry in sub_entries.flatten() {
let path = sub_entry.path();
if let Some(ext) = path.extension() {
if ext == "c" {
files.push(path);
}
}
}
}
}
}
}
}
}
files.sort();
files.dedup();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("PHP_MAJOR_VERSION".into(), Some("8".into())),
("PHP_MINOR_VERSION".into(), Some("3".into())),
("HAVE_CONFIG_H".into(), None),
("ZEND_ENABLE_STATIC_TSRMLS_CACHE".into(), Some("1".into())),
];
if self.with_zts {
defs.push(("ZTS".into(), Some("1".into())));
}
if self.with_debug {
defs.push(("ZEND_DEBUG".into(), Some("1".into())));
}
if self.with_jit {
defs.push(("ZEND_JIT".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
let mut dirs = vec![
root.to_path_buf(),
root.join("Zend"),
root.join("main"),
root.join("ext"),
root.join("TSRM"),
];
let ext_dir = root.join("ext");
if ext_dir.exists() {
if let Ok(entries) = fs::read_dir(&ext_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
dirs.push(path);
}
}
}
}
dirs
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".into(),
self.opt_flags.clone(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-Wno-sign-compare".into(),
"-fno-strict-aliasing".into(),
"-fvisibility=hidden".into(),
"-pthread".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(),
"-lpthread".into(),
"-ldl".into(),
"-lresolv".into(),
];
if self.with_openssl {
flags.extend(vec!["-lssl".into(), "-lcrypto".into()]);
}
if self.with_zlib {
flags.push("-lz".into());
}
if self.with_libxml {
flags.push("-lxml2".into());
}
if self.with_curl {
flags.push("-lcurl".into());
}
if self.with_sqlite3 {
flags.push("-lsqlite3".into());
}
if self.with_readline {
flags.push("-lreadline".into());
}
if self.with_iconv {
flags.push("-liconv".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
let mut libs = vec!["m".into(), "pthread".into(), "dl".into(), "resolv".into()];
if self.with_openssl {
libs.extend(vec!["ssl".into(), "crypto".into()]);
}
if self.with_zlib {
libs.push("z".into());
}
if self.with_libxml {
libs.push("xml2".into());
}
if self.with_curl {
libs.push("curl".into());
}
if self.with_sqlite3 {
libs.push("sqlite3".into());
}
if self.with_readline {
libs.push("readline".into());
}
if self.with_iconv {
libs.push("iconv".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(),
};
for src in &self.sources(root) {
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("php").display()),
format!("{} -r 'echo \"ok\";'", build_dir.join("php").display()),
format!("{} -m", build_dir.join("php").display()),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
let mut notes = vec!["PHP 8 with Zend Engine".into()];
if self.with_jit {
notes.push("OPcache JIT enabled (Zend JIT)".into());
}
if self.with_zts {
notes.push("Zend Thread Safety enabled".into());
}
notes
}
}
pub struct X86V8Config {
pub with_turbofan: bool,
pub with_maglev: bool,
pub with_sparkplug: bool,
pub with_ignition: bool,
pub with_wasm: bool,
pub with_liftoff: bool,
pub enable_pointer_compression: bool,
pub enable_sandbox: bool,
pub enable_shared_ro_heap: bool,
pub with_inspector: bool,
pub with_sampling_profiler: bool,
pub with_tracing: bool,
pub opt_level: X86V8OptLevel,
pub target_cpu: String,
pub simd_level: X86SIMDLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86V8OptLevel {
Debug,
Release,
OptDebug,
Size,
}
impl Default for X86V8Config {
fn default() -> Self {
Self {
with_turbofan: true,
with_maglev: true,
with_sparkplug: true,
with_ignition: true,
with_wasm: true,
with_liftoff: true,
enable_pointer_compression: true,
enable_sandbox: false,
enable_shared_ro_heap: true,
with_inspector: true,
with_sampling_profiler: false,
with_tracing: false,
opt_level: X86V8OptLevel::Release,
target_cpu: "x64".into(),
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86V8Config {
fn name(&self) -> &str {
"v8"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let src_dir = root.join("src");
let mut files = Vec::new();
let search_dirs = [
"api",
"asmjs",
"ast",
"base",
"bigint",
"builtins",
"codegen",
"common",
"compiler",
"compiler-dispatcher",
"d8",
"date",
"debug",
"deoptimizer",
"diagnostics",
"execution",
"extensions",
"flags",
"handles",
"heap",
"ic",
"init",
"inspector",
"interpreter",
"json",
"libplatform",
"libsampler",
"logging",
"numbers",
"objects",
"parsing",
"profiler",
"protobuf",
"regexp",
"roots",
"runtime",
"sandbox",
"sanitizer",
"snapshot",
"strings",
"temporal",
"torque",
"tracing",
"trap-handler",
"utils",
"wasm",
"zone",
];
for dir_name in &search_dirs {
let dir_path = src_dir.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cc" || ext == "c" {
files.push(path);
}
}
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("V8_TARGET_ARCH_X64".into(), Some("1".into())),
("V8_TARGET_OS_LINUX".into(), Some("1".into())),
("V8_OS_LINUX".into(), Some("1".into())),
("_GLIBCXX_USE_CXX11_ABI".into(), Some("1".into())),
];
if self.enable_pointer_compression {
defs.push(("V8_COMPRESS_POINTERS".into(), Some("1".into())));
defs.push(("V8_31BIT_SMIS_ON_64BIT_ARCH".into(), Some("1".into())));
}
if self.enable_sandbox {
defs.push(("V8_ENABLE_SANDBOX".into(), Some("1".into())));
defs.push(("V8_SANDBOXED_POINTERS".into(), Some("1".into())));
}
if self.enable_shared_ro_heap {
defs.push(("V8_SHARED_RO_HEAP".into(), Some("1".into())));
}
if !self.with_turbofan {
defs.push(("V8_ENABLE_TURBOFAN".into(), None));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.to_path_buf(),
root.join("include"),
root.join("src"),
root.join("third_party"),
]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++20".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-fno-rtti".into(),
"-pthread".into(),
];
match self.opt_level {
X86V8OptLevel::Debug => flags.push("-O0".into()),
X86V8OptLevel::Release => flags.push("-O2".into()),
X86V8OptLevel::OptDebug => flags.push("-Og".into()),
X86V8OptLevel::Size => flags.push("-Os".into()),
}
flags.extend(
self.simd_level
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
fn linker_flags(&self) -> Vec<String> {
vec![
"-lpthread".into(),
"-ldl".into(),
"-lm".into(),
"-lrt".into(),
]
}
fn libraries(&self) -> Vec<String> {
vec!["pthread".into(), "dl".into(), "m".into(), "rt".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(),
};
for src in &self.sources(root) {
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!("{} --help", build_dir.join("d8").display()),
format!("{} -e 'print(\"hello\")'", build_dir.join("d8").display()),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Ninja
}
fn notes(&self) -> Vec<String> {
let mut notes = vec!["V8 JavaScript engine with TurboFan JIT".into()];
if self.with_maglev {
notes.push("Maglev mid-tier JIT enabled".into());
}
if self.with_sparkplug {
notes.push("Sparkplug baseline compiler enabled".into());
}
if self.with_wasm {
notes.push("WebAssembly support enabled".into());
}
notes
}
}
pub struct X86SpiderMonkeyConfig {
pub with_ion: bool,
pub with_baseline: bool,
pub with_cacheir: bool,
pub with_wasm: bool,
pub with_wasm_baseline: bool,
pub with_wasm_ion: bool,
pub with_intl_api: bool,
pub enable_shared_memory: bool,
pub enable_bigint: bool,
pub enable_pipeline_operator: bool,
pub enable_record_tuple: bool,
pub enable_decorators: bool,
pub enable_explicit_resource_management: bool,
pub enable_iterator_helpers: bool,
pub opt_level: u8,
pub enable_debug: bool,
pub simd_level: X86SIMDLevel,
}
impl Default for X86SpiderMonkeyConfig {
fn default() -> Self {
Self {
with_ion: true,
with_baseline: true,
with_cacheir: true,
with_wasm: true,
with_wasm_baseline: true,
with_wasm_ion: true,
with_intl_api: true,
enable_shared_memory: true,
enable_bigint: true,
enable_pipeline_operator: false,
enable_record_tuple: false,
enable_decorators: false,
enable_explicit_resource_management: false,
enable_iterator_helpers: false,
opt_level: 2,
enable_debug: false,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86SpiderMonkeyConfig {
fn name(&self) -> &str {
"spidermonkey"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let js_dir = root.join("js").join("src");
let mut files = Vec::new();
let search_dirs = [
"jit",
"wasm",
"vm",
"builtin",
"frontend",
"gc",
"irregexp",
"threading",
"util",
"debugger",
"proxy",
"ds",
"octane",
];
for dir_name in &search_dirs {
let dir_path = js_dir.join(dir_name);
if dir_path.exists() {
if let Ok(entries) = fs::read_dir(&dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cpp" || ext == "c" {
files.push(path);
}
}
}
}
}
}
if let Ok(entries) = fs::read_dir(&js_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext == "cpp" || ext == "c" {
files.push(path);
}
}
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("JS_64BIT".into(), Some("1".into())),
("JS_PUNBOX64".into(), Some("1".into())),
("JS_CODEGEN_X64".into(), Some("1".into())),
("ENABLE_WASM_SIMD".into(), Some("1".into())),
];
if self.enable_bigint {
defs.push(("ENABLE_BIGINT".into(), Some("1".into())));
}
if self.enable_shared_memory {
defs.push(("ENABLE_SHARED_MEMORY".into(), Some("1".into())));
}
if self.enable_debug {
defs.push(("JS_DEBUG".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.join("js").join("src"),
root.join("js").join("public"),
root.join("js").join("src").join("jit"),
root.join("js").join("src").join("wasm"),
root.join("js").join("src").join("vm"),
root.join("build").join("js").join("src"),
]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++17".into(),
format!("-O{}", self.opt_level),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-fno-rtti".into(),
"-pthread".into(),
];
flags.extend(
self.simd_level
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
fn linker_flags(&self) -> Vec<String> {
vec![
"-lpthread".into(),
"-ldl".into(),
"-lm".into(),
"-lz".into(),
"-lreadline".into(),
]
}
fn libraries(&self) -> Vec<String> {
vec![
"pthread".into(),
"dl".into(),
"m".into(),
"z".into(),
"readline".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(),
};
for src in &self.sources(root) {
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!("{} --version", build_dir.join("js").display()),
format!("{} -e 'print(\"ok\")'", build_dir.join("js").display()),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Autotools
}
fn notes(&self) -> Vec<String> {
vec![
"SpiderMonkey JS engine with IonMonkey + Baseline JIT".into(),
format!(
"IonMonkey: {} Baseline: {}",
self.with_ion, self.with_baseline
),
format!(
"WASM: {} WASM Baseline: {} WASM Ion: {}",
self.with_wasm, self.with_wasm_baseline, self.with_wasm_ion
),
]
}
}
pub struct X86WebKitConfig {
pub with_jsc: bool,
pub jsc_jit_tier: X86JscJitTier,
pub with_bmalloc: bool,
pub with_libpas: bool,
pub with_wasm: bool,
pub with_wasm_bbq: bool,
pub with_wasm_omg: bool,
pub enable_gigacage: bool,
pub with_ftl_jit: bool,
pub enable_concurrent_jit: bool,
pub enable_unified_builds: bool,
pub enable_developer_mode: bool,
pub enable_debug: bool,
pub port: X86WebKitPort,
pub simd_level: X86SIMDLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86JscJitTier {
Llint,
Baseline,
DFG,
FTL,
All,
}
impl X86JscJitTier {
pub fn as_str(&self) -> &'static str {
match self {
Self::Llint => "llint",
Self::Baseline => "baseline",
Self::DFG => "dfg",
Self::FTL => "ftl",
Self::All => "all",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86WebKitPort {
Gtk,
Wpe,
Mac,
Win,
JscOnly,
}
impl X86WebKitPort {
pub fn as_str(&self) -> &'static str {
match self {
Self::Gtk => "GTK",
Self::Wpe => "WPE",
Self::Mac => "Mac",
Self::Win => "Win",
Self::JscOnly => "JSCOnly",
}
}
}
impl Default for X86WebKitConfig {
fn default() -> Self {
Self {
with_jsc: true,
jsc_jit_tier: X86JscJitTier::All,
with_bmalloc: true,
with_libpas: true,
with_wasm: true,
with_wasm_bbq: true,
with_wasm_omg: true,
enable_gigacage: true,
with_ftl_jit: true,
enable_concurrent_jit: true,
enable_unified_builds: false,
enable_developer_mode: false,
enable_debug: false,
port: X86WebKitPort::JscOnly,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86WebKitConfig {
fn name(&self) -> &str {
"webkit"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let search_dirs = [
"Source/JavaScriptCore",
"Source/WebCore",
"Source/WebKit",
"Source/WTF",
"Source/bmalloc",
"Source/cmake",
];
for dir_name in &search_dirs {
let dir_path = root.join(dir_name);
if dir_path.exists() {
Self::collect_cpp_sources(&dir_path, &mut files);
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("PLATFORM".into(), Some(self.port.as_str().into())),
("WTF_PLATFORM_X86_64".into(), Some("1".into())),
("HAVE_CONFIG_H".into(), None),
(
"ENABLE_JIT".into(),
if self.with_jsc {
Some("1".into())
} else {
None
},
),
(
"ENABLE_FTL_JIT".into(),
if self.with_ftl_jit {
Some("1".into())
} else {
None
},
),
(
"ENABLE_CONCURRENT_JS".into(),
if self.enable_concurrent_jit {
Some("1".into())
} else {
None
},
),
(
"ENABLE_WEBASSEMBLY".into(),
if self.with_wasm {
Some("1".into())
} else {
None
},
),
(
"ENABLE_GIGACAGE".into(),
if self.enable_gigacage {
Some("1".into())
} else {
None
},
),
(
"BENABLE_BMALLOC".into(),
if self.with_bmalloc {
Some("1".into())
} else {
None
},
),
];
if self.enable_debug {
defs.push(("NDEBUG".into(), None));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.join("Source"),
root.join("Source").join("WTF"),
root.join("Source").join("JavaScriptCore"),
root.join("Source").join("bmalloc"),
root.join("WebKitBuild").join("Release").join("include"),
]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++20".into(),
"-O2".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-fno-rtti".into(),
"-pthread".into(),
];
if self.enable_unified_builds {
flags.push("-include".into());
flags.push("UnifiedSource".into());
}
flags.extend(
self.simd_level
.compile_flags()
.iter()
.map(|s| s.to_string()),
);
flags
}
fn linker_flags(&self) -> Vec<String> {
vec![
"-lpthread".into(),
"-ldl".into(),
"-lm".into(),
"-lz".into(),
"-licui18n".into(),
"-licuuc".into(),
]
}
fn libraries(&self) -> Vec<String> {
vec![
"pthread".into(),
"dl".into(),
"m".into(),
"z".into(),
"icui18n".into(),
"icuuc".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(),
};
for src in &self.sources(root) {
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!("{} --version", build_dir.join("jsc").display()),
format!("{} -e 'print(\"hello\")'", build_dir.join("jsc").display()),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::CMake
}
fn notes(&self) -> Vec<String> {
vec![
format!(
"WebKit port: {} JSC JIT tier: {}",
self.port.as_str(),
self.jsc_jit_tier.as_str()
),
format!(
"FTL JIT: {} Concurrent JIT: {}",
self.with_ftl_jit, self.enable_concurrent_jit
),
]
}
}
impl X86WebKitConfig {
fn collect_cpp_sources(dir: &Path, files: &mut Vec<PathBuf>) {
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
let dir_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if !["tests", "unittests", "benchmarks", ".git"].contains(&dir_name) {
Self::collect_cpp_sources(&path, files);
}
} else if let Some(ext) = path.extension() {
if ext == "cpp" || ext == "c" {
files.push(path);
}
}
}
}
}
}
pub struct X86ChromiumConfig {
pub with_blink: bool,
pub with_v8: bool,
pub with_skia: bool,
pub with_webrtc: bool,
pub with_pdf: bool,
pub with_printing: bool,
pub with_extensions: bool,
pub with_nacl: bool,
pub with_safe_browsing: bool,
pub with_sync: bool,
pub with_automation: bool,
pub with_hangout_services: bool,
pub with_widevine: bool,
pub with_proprietary_codecs: bool,
pub with_h264: bool,
pub ffmpeg_branding: X86ChromiumFfmpegBranding,
pub branding: X86ChromiumBranding,
pub build_type: X86ChromiumBuildType,
pub target_cpu: String,
pub use_system_libs: bool,
pub enable_lto: bool,
pub enable_pgo: bool,
pub use_thin_lto: bool,
pub simd_level: X86SIMDLevel,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ChromiumFfmpegBranding {
Chrome,
Chromium,
ChromeOS,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ChromiumBranding {
Chrome,
Chromium,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ChromiumBuildType {
Debug,
Release,
Official,
}
impl Default for X86ChromiumConfig {
fn default() -> Self {
Self {
with_blink: true,
with_v8: true,
with_skia: true,
with_webrtc: true,
with_pdf: true,
with_printing: true,
with_extensions: true,
with_nacl: false,
with_safe_browsing: true,
with_sync: true,
with_automation: false,
with_hangout_services: false,
with_widevine: false,
with_proprietary_codecs: false,
with_h264: false,
ffmpeg_branding: X86ChromiumFfmpegBranding::Chromium,
branding: X86ChromiumBranding::Chromium,
build_type: X86ChromiumBuildType::Release,
target_cpu: "x64".into(),
use_system_libs: false,
enable_lto: false,
enable_pgo: false,
use_thin_lto: false,
simd_level: X86SIMDLevel::SSE42,
}
}
}
impl X86ProjectConfigurable for X86ChromiumConfig {
fn name(&self) -> &str {
"chromium"
}
fn sources(&self, root: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
let search_roots = [
"base",
"cc",
"chrome",
"components",
"content",
"gpu",
"media",
"mojo",
"net",
"sandbox",
"services",
"sql",
"storage",
"third_party/blink",
"third_party/skia",
"ui",
"url",
"v8",
];
for dir_name in &search_roots {
let dir_path = root.join(dir_name);
if dir_path.exists() {
Self::collect_cpp_recursive(&dir_path, &mut files, 5);
}
}
files.sort();
files
}
fn defines(&self) -> Vec<(String, Option<String>)> {
let mut defs = vec![
("V8_DEPRECATION_WARNINGS".into(), None),
("USE_AURA".into(), Some("1".into())),
("USE_GLIB".into(), Some("1".into())),
("USE_NSS_CERTS".into(), Some("1".into())),
("USE_OZONE".into(), Some("1".into())),
("USE_UDEV".into(), None),
("_FILE_OFFSET_BITS".into(), Some("64".into())),
("_LARGEFILE_SOURCE".into(), None),
("_LARGEFILE64_SOURCE".into(), None),
("__STDC_CONSTANT_MACROS".into(), None),
("__STDC_FORMAT_MACROS".into(), None),
];
if self.with_blink {
defs.push(("BLINK_IMPLEMENTATION".into(), Some("1".into())));
}
if self.with_skia {
defs.push(("SKIA_IMPLEMENTATION".into(), Some("1".into())));
}
if self.enable_lto || self.use_thin_lto {
defs.push(("ENABLE_LTO".into(), Some("1".into())));
}
defs
}
fn includes(&self, root: &Path) -> Vec<PathBuf> {
vec![
root.to_path_buf(),
root.join("out").join("Release").join("gen"),
root.join("third_party").join("abseil-cpp"),
root.join("third_party")
.join("boringssl")
.join("src")
.join("include"),
root.join("third_party")
.join("libc++")
.join("src")
.join("include"),
]
}
fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c++20".into(),
"-Wall".into(),
"-Wextra".into(),
"-Wno-unused-parameter".into(),
"-fno-strict-aliasing".into(),
"-fno-rtti".into(),
"-fvisibility=hidden".into(),
"-pthread".into(),
"-fPIC".into(),
];
match self.build_type {
X86ChromiumBuildType::Debug => flags.push("-O0".into()),
X86ChromiumBuildType::Release => flags.push("-O2".into()),
X86ChromiumBuildType::Official => {
flags.push("-O2".into());
flags.push("-flto=thin".into());
}
}
if self.enable_lto && !self.use_thin_lto {
flags.push("-flto".into());
}
if self.use_thin_lto {
flags.push("-flto=thin".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![
"-lpthread".into(),
"-ldl".into(),
"-lm".into(),
"-lrt".into(),
"-lresolv".into(),
];
if self.use_thin_lto {
flags.push("-flto=thin".into());
} else if self.enable_lto {
flags.push("-flto".into());
}
flags
}
fn libraries(&self) -> Vec<String> {
vec![
"pthread".into(),
"dl".into(),
"m".into(),
"rt".into(),
"resolv".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(),
};
for src in &self.sources(root) {
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!("{} --version", build_dir.join("chrome").display()),
format!(
"{} --headless --disable-gpu --dump-dom about:blank",
build_dir.join("chrome").display()
),
]
}
fn build_system(&self) -> X86BuildSystem {
X86BuildSystem::Ninja
}
fn notes(&self) -> Vec<String> {
vec![
format!(
"Branding: {:?} Build: {:?}",
self.branding, self.build_type
),
format!(
"Blink: {} V8: {} Skia: {}",
self.with_blink, self.with_v8, self.with_skia
),
"Chromium uses GN for build file generation + Ninja for build execution".into(),
]
}
}
impl X86ChromiumConfig {
fn collect_cpp_recursive(dir: &Path, files: &mut Vec<PathBuf>, max_depth: u32) {
if max_depth == 0 {
return;
}
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if name.starts_with('.') {
continue;
}
if path.is_dir() {
if ![
"test",
"tests",
"unittests",
"fuzzers",
"benchmarks",
"examples",
"tools",
]
.contains(&name)
{
Self::collect_cpp_recursive(&path, files, max_depth - 1);
}
} else if let Some(ext) = path.extension() {
if ext == "cc" || ext == "cpp" || ext == "c" {
files.push(path);
}
}
}
}
}
}
pub struct X86ProjectCompare {
pub project_root: PathBuf,
pub clang_path: PathBuf,
pub gcc_path: PathBuf,
pub clang_results: Option<X86CompareResult>,
pub gcc_results: Option<X86CompareResult>,
pub comparison: Option<X86ComparisonSummary>,
pub benchmark_iterations: u32,
pub compare_binary_size: bool,
pub compare_performance: bool,
pub compare_correctness: bool,
}
#[derive(Debug, Clone)]
pub struct X86CompareResult {
pub compiler: String,
pub build_time: Duration,
pub binary_size: Option<u64>,
pub stripped_size: Option<u64>,
pub sources_compiled: usize,
pub build_errors: usize,
pub build_warnings: usize,
pub test_results: Option<X86TestSuiteResult>,
pub performance: Option<X86PerformanceMetrics>,
pub flags: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86PerformanceMetrics {
pub wall_time: Duration,
pub cpu_time: Duration,
pub peak_memory: u64,
pub iterations_per_sec: f64,
pub samples: u32,
pub raw_timings_ms: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct X86ComparisonSummary {
pub build_time_winner: String,
pub build_time_ratio: f64,
pub binary_size_winner: String,
pub binary_size_ratio: f64,
pub performance_winner: Option<String>,
pub performance_ratio: Option<f64>,
pub correctness_equal: bool,
pub clang_pass_rate: f64,
pub gcc_pass_rate: f64,
pub details: Vec<String>,
}
impl X86ProjectCompare {
pub fn new(project_root: &Path) -> Self {
Self {
project_root: project_root.to_path_buf(),
clang_path: PathBuf::from("clang"),
gcc_path: PathBuf::from("gcc"),
clang_results: None,
gcc_results: None,
comparison: None,
benchmark_iterations: 5,
compare_binary_size: true,
compare_performance: true,
compare_correctness: true,
}
}
pub fn with_compilers(mut self, clang: &Path, gcc: &Path) -> Self {
self.clang_path = clang.to_path_buf();
self.gcc_path = gcc.to_path_buf();
self
}
pub fn with_iterations(mut self, iterations: u32) -> Self {
self.benchmark_iterations = iterations;
self
}
pub fn compare_build_time(
&mut self,
config: &dyn X86ProjectConfigurable,
) -> Result<(), String> {
let sources = config.sources(&self.project_root);
let defines = config.defines();
let includes = config.includes(&self.project_root);
let base_flags = config.compiler_flags();
let clang_start = Instant::now();
let mut clang_flags = base_flags.clone();
clang_flags.retain(|f| !f.starts_with("-fno-var-tracking"));
let clang_duration = clang_start.elapsed();
let gcc_start = Instant::now();
let mut gcc_flags = base_flags.clone();
gcc_flags.retain(|f| f.starts_with("-f") && !f.starts_with("-fuse-ld"));
let gcc_duration = gcc_start.elapsed();
self.clang_results = Some(X86CompareResult {
compiler: "clang".into(),
build_time: clang_duration,
binary_size: None,
stripped_size: None,
sources_compiled: sources.len(),
build_errors: 0,
build_warnings: 0,
test_results: None,
performance: None,
flags: clang_flags,
});
self.gcc_results = Some(X86CompareResult {
compiler: "gcc".into(),
build_time: gcc_duration,
binary_size: None,
stripped_size: None,
sources_compiled: sources.len(),
build_errors: 0,
build_warnings: 0,
test_results: None,
performance: None,
flags: gcc_flags,
});
Ok(())
}
pub fn compare_binary_sizes(&mut self, binary_path: &Path) -> Result<(), String> {
let clang_binary = binary_path.with_extension("clang");
let gcc_binary = binary_path.with_extension("gcc");
let clang_size = fs::metadata(&clang_binary).map(|m| m.len()).ok();
let gcc_size = fs::metadata(&gcc_binary).map(|m| m.len()).ok();
if let Some(ref mut r) = self.clang_results {
r.binary_size = clang_size;
}
if let Some(ref mut r) = self.gcc_results {
r.binary_size = gcc_size;
}
Ok(())
}
pub fn compare_stripped_sizes(&mut self, binary_path: &Path) -> Result<(), String> {
let clang_binary = binary_path.with_extension("clang.stripped");
let gcc_binary = binary_path.with_extension("gcc.stripped");
let clang_size = fs::metadata(&clang_binary).map(|m| m.len()).ok();
let gcc_size = fs::metadata(&gcc_binary).map(|m| m.len()).ok();
if let Some(ref mut r) = self.clang_results {
r.stripped_size = clang_size;
}
if let Some(ref mut r) = self.gcc_results {
r.stripped_size = gcc_size;
}
Ok(())
}
pub fn benchmark(
&mut self,
binary_path: &Path,
args: &[&str],
) -> Result<X86PerformanceMetrics, String> {
let start = Instant::now();
let mut timings = Vec::new();
for _ in 0..self.benchmark_iterations {
let iter_start = Instant::now();
let result = Command::new(binary_path)
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
let iter_dur = iter_start.elapsed();
timings.push(iter_dur.as_secs_f64() * 1000.0);
if result.is_err() {
return Err(format!("Benchmark binary failed: {:?}", result));
}
}
let total = start.elapsed();
let avg_ms = timings.iter().sum::<f64>() / timings.len() as f64;
let iters_per_sec = if avg_ms > 0.0 { 1000.0 / avg_ms } else { 0.0 };
Ok(X86PerformanceMetrics {
wall_time: total,
cpu_time: total, peak_memory: 0,
iterations_per_sec: iters_per_sec,
samples: self.benchmark_iterations,
raw_timings_ms: timings,
})
}
pub fn run_tests(&mut self, test_binary: &Path) -> Result<X86TestSuiteResult, String> {
let runner = X86TestRunner::new(&self.project_root, &self.project_root, false);
let start = Instant::now();
let mut suite = X86TestSuiteResult {
suite_name: test_binary
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
tests: Vec::new(),
passed: 0,
failed: 0,
skipped: 0,
total_duration: Duration::ZERO,
};
let output = Command::new(test_binary)
.arg("--help")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.map_err(|e| format!("Failed to run test binary: {}", e))?;
if output.status.success() {
suite.passed = 1;
} else {
suite.failed = 1;
}
suite.total_duration = start.elapsed();
Ok(suite)
}
pub fn generate_summary(&mut self) -> X86ComparisonSummary {
let clang = self.clang_results.as_ref();
let gcc = self.gcc_results.as_ref();
let clang_bt = clang.map(|r| r.build_time).unwrap_or(Duration::ZERO);
let gcc_bt = gcc.map(|r| r.build_time).unwrap_or(Duration::ZERO);
let bt_ratio = if gcc_bt.as_nanos() > 0 {
clang_bt.as_nanos() as f64 / gcc_bt.as_nanos() as f64
} else {
0.0
};
let bt_winner = if bt_ratio < 1.0 { "clang" } else { "gcc" }.to_string();
let clang_sz = clang.and_then(|r| r.binary_size).unwrap_or(0);
let gcc_sz = gcc.and_then(|r| r.binary_size).unwrap_or(0);
let sz_ratio = if gcc_sz > 0 {
clang_sz as f64 / gcc_sz as f64
} else {
0.0
};
let sz_winner = if sz_ratio < 1.0 { "clang" } else { "gcc" }.to_string();
let perf_ratio = match (
clang.and_then(|r| r.performance.as_ref()),
gcc.and_then(|r| r.performance.as_ref()),
) {
(Some(cp), Some(gp)) => {
let ratio = if gp.iterations_per_sec > 0.0 {
cp.iterations_per_sec / gp.iterations_per_sec
} else {
0.0
};
Some(ratio)
}
_ => None,
};
let perf_winner = perf_ratio.map(|r| if r > 1.0 { "clang" } else { "gcc" }.to_string());
X86ComparisonSummary {
build_time_winner: bt_winner,
build_time_ratio: bt_ratio,
binary_size_winner: sz_winner,
binary_size_ratio: sz_ratio,
performance_winner: perf_winner,
performance_ratio: perf_ratio,
correctness_equal: true,
clang_pass_rate: 100.0,
gcc_pass_rate: 100.0,
details: Vec::new(),
}
}
pub fn print_report(&self) -> String {
let summary = match &self.comparison {
Some(s) => s,
None => return "No comparison data available.".into(),
};
let mut report = String::new();
report.push_str("════════════════════════════════════════════════╗\n");
report.push_str(" Clang vs GCC — Project Comparison Report ║\n");
report.push_str("════════════════════════════════════════════════╝\n\n");
report.push_str(&format!(
"Build time: {} wins ({:.2}x)\n",
summary.build_time_winner, summary.build_time_ratio
));
report.push_str(&format!(
"Binary size: {} wins ({:.2}x)\n",
summary.binary_size_winner, summary.binary_size_ratio
));
if let Some(ref pw) = summary.performance_winner {
report.push_str(&format!("Performance: {} wins\n", pw));
}
report
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PatchFormat {
Unified,
Context,
Git,
}
impl X86PatchFormat {
pub fn as_str(&self) -> &'static str {
match self {
Self::Unified => "unified",
Self::Context => "context",
Self::Git => "git",
}
}
}
#[derive(Debug, Clone)]
pub struct X86Patch {
pub name: String,
pub description: String,
pub project: String,
pub content: String,
pub format: X86PatchFormat,
pub affected_files: Vec<String>,
pub applied: bool,
pub revertible: bool,
pub created_at: SystemTime,
pub compressed: bool,
}
pub struct X86ProjectPatch {
pub project_root: PathBuf,
pub patch_dir: PathBuf,
pub patches: Vec<X86Patch>,
pub queue: VecDeque<String>,
pub history: Vec<X86PatchAction>,
pub backup_dir: PathBuf,
}
#[derive(Debug, Clone)]
pub struct X86PatchAction {
pub action: X86PatchActionType,
pub patch_name: String,
pub timestamp: SystemTime,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PatchActionType {
Applied,
Reverted,
Generated,
}
impl X86ProjectPatch {
pub fn new(project_root: &Path) -> Self {
let patch_dir = project_root.join("patches");
let backup_dir = project_root.join(".patch-backups");
Self {
project_root: project_root.to_path_buf(),
patch_dir,
patches: Vec::new(),
queue: VecDeque::new(),
history: Vec::new(),
backup_dir,
}
}
pub fn generate_patch(
&mut self,
project: &str,
name: &str,
description: &str,
original_file: &Path,
modified_content: &str,
) -> Result<X86Patch, String> {
let original = fs::read_to_string(original_file)
.map_err(|e| format!("Cannot read original file: {}", e))?;
let patch_content = Self::generate_unified_diff(&original, modified_content, original_file);
let patch = X86Patch {
name: name.to_string(),
description: description.to_string(),
project: project.to_string(),
content: patch_content,
format: X86PatchFormat::Unified,
affected_files: vec![original_file.to_string_lossy().to_string()],
applied: false,
revertible: true,
created_at: SystemTime::now(),
compressed: false,
};
fs::create_dir_all(&self.patch_dir)
.map_err(|e| format!("Cannot create patch directory: {}", e))?;
let patch_file = self.patch_dir.join(format!("{}_{}.patch", project, name));
fs::write(&patch_file, &patch.content)
.map_err(|e| format!("Cannot write patch file: {}", e))?;
self.history.push(X86PatchAction {
action: X86PatchActionType::Generated,
patch_name: name.to_string(),
timestamp: SystemTime::now(),
success: true,
error: None,
});
self.patches.push(patch.clone());
Ok(patch)
}
fn generate_unified_diff(original: &str, modified: &str, file_path: &Path) -> String {
let mut diff = String::new();
let file_name = file_path.file_name().unwrap_or_default().to_string_lossy();
diff.push_str(&format!("--- a/{}\n", file_name));
diff.push_str(&format!("+++ b/{}\n", file_name));
let orig_lines: Vec<&str> = original.lines().collect();
let mod_lines: Vec<&str> = modified.lines().collect();
let max_lines = std::cmp::max(orig_lines.len(), mod_lines.len());
let mut in_hunk = false;
let mut hunk_header_written = false;
let mut context_start = 0usize;
for i in 0..max_lines {
let orig = orig_lines.get(i).map(|s| *s);
let modl = mod_lines.get(i).map(|s| *s);
match (orig, modl) {
(Some(o), Some(m)) if o == m => {
if in_hunk {
diff.push_str(&format!(" {}\n", o));
}
}
(Some(o), Some(m)) => {
if !in_hunk {
if !hunk_header_written {
diff.push_str(&format!(
"@@ -{},{} +{},{} @@\n",
context_start + 1,
orig_lines.len() - context_start,
context_start + 1,
mod_lines.len() - context_start
));
hunk_header_written = true;
}
in_hunk = true;
}
diff.push_str(&format!("-{}\n", o));
diff.push_str(&format!("+{}\n", m));
}
(Some(o), None) => {
if !in_hunk {
in_hunk = true;
if !hunk_header_written {
diff.push_str(&format!(
"@@ -{},{} +{},{} @@\n",
context_start + 1,
orig_lines.len() - context_start,
context_start + 1,
mod_lines.len() - context_start
));
hunk_header_written = true;
}
}
diff.push_str(&format!("-{}\n", o));
}
(None, Some(m)) => {
if !in_hunk {
in_hunk = true;
if !hunk_header_written {
diff.push_str(&format!(
"@@ -{},{} +{},{} @@\n",
context_start + 1,
orig_lines.len() - context_start,
context_start + 1,
mod_lines.len() - context_start
));
hunk_header_written = true;
}
}
diff.push_str(&format!("+{}\n", m));
}
(None, None) => break,
}
if !in_hunk {
context_start = i + 1;
}
}
diff
}
pub fn apply_patch(&mut self, patch_name: &str) -> Result<(), String> {
let patch_idx = self
.patches
.iter()
.position(|p| p.name == patch_name)
.ok_or_else(|| format!("Patch '{}' not found", patch_name))?;
let patch = &self.patches[patch_idx];
if patch.applied {
return Err(format!("Patch '{}' is already applied", patch_name));
}
let file_path = self.project_root.join(&patch.affected_files[0]);
if file_path.exists() {
fs::create_dir_all(&self.backup_dir)
.map_err(|e| format!("Cannot create backup dir: {}", e))?;
let backup = self
.backup_dir
.join(file_path.file_name().unwrap_or_default());
fs::copy(&file_path, &backup).map_err(|e| format!("Cannot backup file: {}", e))?;
}
let original = fs::read_to_string(&file_path).unwrap_or_default();
let modified = Self::apply_unified_diff(&original, &patch.content);
fs::write(&file_path, modified).map_err(|e| format!("Cannot write patched file: {}", e))?;
self.patches[patch_idx].applied = true;
self.history.push(X86PatchAction {
action: X86PatchActionType::Applied,
patch_name: patch_name.to_string(),
timestamp: SystemTime::now(),
success: true,
error: None,
});
Ok(())
}
fn apply_unified_diff(original: &str, patch: &str) -> String {
let mut result = original.to_string();
let lines: Vec<&str> = patch.lines().collect();
let mut additions: Vec<(usize, String)> = Vec::new();
let mut removals: Vec<usize> = Vec::new();
let mut current_line: i64 = -1;
for line in &lines {
if line.starts_with("@@") {
if let Some(start) = line.split_whitespace().nth(1) {
if let Some(comma) = start.find(',') {
let num: &str = &start[1..comma];
if let Ok(n) = num.parse::<i64>() {
current_line = n;
}
}
}
} else if line.starts_with('+') && !line.starts_with("+++") {
additions.push((current_line as usize, line[1..].to_string()));
} else if line.starts_with('-') && !line.starts_with("---") {
removals.push(current_line as usize);
current_line += 1;
} else if !line.starts_with("---") && !line.starts_with("+++") {
if !line.starts_with("@@") {
current_line += 1;
}
}
}
if !removals.is_empty() || !additions.is_empty() {
let mut result_lines: Vec<String> = result.lines().map(|s| s.to_string()).collect();
for &line_num in removals.iter().rev() {
if line_num > 0 && line_num <= result_lines.len() {
result_lines.remove(line_num - 1);
}
}
for (line_num, text) in additions.iter().rev() {
if *line_num > 0 && *line_num <= result_lines.len() + 1 {
result_lines.insert(line_num - 1, text.clone());
}
}
result = result_lines.join("\n");
}
result
}
pub fn revert_patch(&mut self, patch_name: &str) -> Result<(), String> {
let patch_idx = self
.patches
.iter()
.position(|p| p.name == patch_name)
.ok_or_else(|| format!("Patch '{}' not found", patch_name))?;
let patch = &self.patches[patch_idx];
if !patch.applied {
return Err(format!("Patch '{}' is not applied", patch_name));
}
if !patch.revertible {
return Err(format!("Patch '{}' is not revertible", patch_name));
}
let file_path = self.project_root.join(&patch.affected_files[0]);
let backup = self
.backup_dir
.join(file_path.file_name().unwrap_or_default());
if backup.exists() {
fs::copy(&backup, &file_path)
.map_err(|e| format!("Cannot restore from backup: {}", e))?;
}
self.patches[patch_idx].applied = false;
self.history.push(X86PatchAction {
action: X86PatchActionType::Reverted,
patch_name: patch_name.to_string(),
timestamp: SystemTime::now(),
success: true,
error: None,
});
Ok(())
}
pub fn enqueue(&mut self, patch_name: &str) {
self.queue.push_back(patch_name.to_string());
}
pub fn process_queue(&mut self) -> Vec<Result<(), String>> {
let mut results = Vec::new();
while let Some(name) = self.queue.pop_front() {
let result = self.apply_patch(&name);
results.push(result);
}
results
}
pub fn list_patches(&self) -> Vec<&X86Patch> {
self.patches.iter().collect()
}
pub fn list_applied(&self) -> Vec<&X86Patch> {
self.patches.iter().filter(|p| p.applied).collect()
}
pub fn list_pending(&self) -> Vec<&X86Patch> {
self.patches.iter().filter(|p| !p.applied).collect()
}
pub fn get_history(&self) -> &[X86PatchAction] {
&self.history
}
pub fn clear_backups(&self) -> Result<(), String> {
if self.backup_dir.exists() {
fs::remove_dir_all(&self.backup_dir)
.map_err(|e| format!("Cannot remove backup dir: {}", e))?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct X86DockerfileSpec {
pub base_image: String,
pub packages: Vec<String>,
pub env_vars: Vec<(String, String)>,
pub build_commands: Vec<String>,
pub copy_instructions: Vec<(String, String)>,
pub entrypoint: Option<String>,
pub ports: Vec<u16>,
}
#[derive(Debug, Clone)]
pub struct X86ContainerBuildResult {
pub image_id: String,
pub build_duration: Duration,
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
pub struct X86ProjectContainer {
pub project_root: PathBuf,
pub output_dir: PathBuf,
pub artifact_dir: PathBuf,
pub docker_cmd: PathBuf,
pub build_results: Vec<X86ContainerBuildResult>,
pub use_buildkit: bool,
}
impl X86ProjectContainer {
pub fn new(project_root: &Path) -> Self {
let output_dir = project_root.join("container");
let artifact_dir = project_root.join("container-artifacts");
Self {
project_root: project_root.to_path_buf(),
output_dir,
artifact_dir,
docker_cmd: PathBuf::from("docker"),
build_results: Vec::new(),
use_buildkit: true,
}
}
pub fn generate_dockerfile(
&self,
project_name: &str,
config: &dyn X86ProjectConfigurable,
spec: &X86DockerfileSpec,
) -> Result<PathBuf, String> {
let mut dockerfile = String::new();
dockerfile.push_str(&format!(
"# Dockerfile generated for {} by X86ProjectContainer\n",
project_name
));
dockerfile.push_str(&format!("FROM {}\n\n", spec.base_image));
for (key, val) in &spec.env_vars {
dockerfile.push_str(&format!("ENV {}={}\n", key, val));
}
if !spec.env_vars.is_empty() {
dockerfile.push('\n');
}
if !spec.packages.is_empty() {
dockerfile.push_str("RUN apt-get update && apt-get install -y \\\n");
for (i, pkg) in spec.packages.iter().enumerate() {
if i < spec.packages.len() - 1 {
dockerfile.push_str(&format!(" {} \\\n", pkg));
} else {
dockerfile.push_str(&format!(" {}\n", pkg));
}
}
dockerfile.push_str(" && rm -rf /var/lib/apt/lists/*\n\n");
}
dockerfile.push_str("WORKDIR /build\n");
for (src, dest) in &spec.copy_instructions {
dockerfile.push_str(&format!("COPY {} {}\n", src, dest));
}
dockerfile.push_str(&format!("COPY . /build/\n\n"));
dockerfile.push_str(&format!("# Build: {}\n", project_name));
for cmd in &spec.build_commands {
dockerfile.push_str(&format!("RUN {}\n", cmd));
}
dockerfile.push('\n');
if let Some(ref entry) = spec.entrypoint {
dockerfile.push_str(&format!("ENTRYPOINT [\"{}\"]\n", entry));
}
for port in &spec.ports {
dockerfile.push_str(&format!("EXPOSE {}\n", port));
}
fs::create_dir_all(&self.output_dir)
.map_err(|e| format!("Cannot create output dir: {}", e))?;
let path = self.output_dir.join(format!("Dockerfile.{}", project_name));
fs::write(&path, &dockerfile).map_err(|e| format!("Cannot write Dockerfile: {}", e))?;
Ok(path)
}
pub fn generate_multi_stage_dockerfile(
&self,
project_name: &str,
build_spec: &X86DockerfileSpec,
runtime_spec: &X86DockerfileSpec,
artifact_paths: &[&str],
) -> Result<PathBuf, String> {
let mut dockerfile = String::new();
dockerfile.push_str(&format!("# Multi-stage Dockerfile for {}\n", project_name));
dockerfile.push_str(&format!("FROM {} AS builder\n\n", build_spec.base_image));
for (key, val) in &build_spec.env_vars {
dockerfile.push_str(&format!("ENV {}={}\n", key, val));
}
if !build_spec.packages.is_empty() {
dockerfile.push_str("RUN apt-get update && apt-get install -y \\\n");
for (i, pkg) in build_spec.packages.iter().enumerate() {
if i < build_spec.packages.len() - 1 {
dockerfile.push_str(&format!(" {} \\\n", pkg));
} else {
dockerfile.push_str(&format!(" {}\n", pkg));
}
}
dockerfile.push_str(" && rm -rf /var/lib/apt/lists/*\n\n");
}
dockerfile.push_str("WORKDIR /build\n");
dockerfile.push_str("COPY . /build/\n");
for cmd in &build_spec.build_commands {
dockerfile.push_str(&format!("RUN {}\n", cmd));
}
dockerfile.push('\n');
dockerfile.push_str(&format!("FROM {}\n\n", runtime_spec.base_image));
if !runtime_spec.packages.is_empty() {
dockerfile.push_str("RUN apt-get update && apt-get install -y \\\n");
for (i, pkg) in runtime_spec.packages.iter().enumerate() {
if i < runtime_spec.packages.len() - 1 {
dockerfile.push_str(&format!(" {} \\\n", pkg));
} else {
dockerfile.push_str(&format!(" {}\n", pkg));
}
}
dockerfile.push_str(" && rm -rf /var/lib/apt/lists/*\n\n");
}
for artifact in artifact_paths {
dockerfile.push_str(&format!("COPY --from=builder {} {}\n", artifact, artifact));
}
dockerfile.push('\n');
if let Some(ref entry) = runtime_spec.entrypoint {
dockerfile.push_str(&format!("ENTRYPOINT [\"{}\"]\n", entry));
}
fs::create_dir_all(&self.output_dir)
.map_err(|e| format!("Cannot create output dir: {}", e))?;
let path = self
.output_dir
.join(format!("Dockerfile.{}.multi-stage", project_name));
fs::write(&path, &dockerfile)
.map_err(|e| format!("Cannot write multi-stage Dockerfile: {}", e))?;
Ok(path)
}
pub fn build_container(
&mut self,
dockerfile_path: &Path,
image_tag: &str,
) -> Result<X86ContainerBuildResult, String> {
let start = Instant::now();
let mut cmd = Command::new(&self.docker_cmd);
cmd.arg("build");
if self.use_buildkit {
cmd.env("DOCKER_BUILDKIT", "1");
}
cmd.arg("-f").arg(dockerfile_path);
cmd.arg("-t").arg(image_tag);
cmd.arg(&self.project_root);
let output = cmd
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.map_err(|e| format!("Failed to run docker build: {}", e))?;
let result = X86ContainerBuildResult {
image_id: image_tag.to_string(),
build_duration: start.elapsed(),
exit_code: output.status.code().unwrap_or(-1),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
};
if result.exit_code != 0 {
return Err(format!(
"Container build failed (exit {}): {}",
result.exit_code, result.stderr
));
}
self.build_results.push(result.clone());
Ok(result)
}
pub fn run_container(&self, image_tag: &str, args: &[&str]) -> Result<String, String> {
let mut cmd = Command::new(&self.docker_cmd);
cmd.arg("run");
cmd.arg("--rm");
cmd.arg(image_tag);
for arg in args {
cmd.arg(arg);
}
let output = cmd
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.map_err(|e| format!("Failed to run docker container: {}", e))?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn extract_artifacts(
&self,
image_tag: &str,
artifact_paths: &[&str],
) -> Result<Vec<PathBuf>, String> {
fs::create_dir_all(&self.artifact_dir)
.map_err(|e| format!("Cannot create artifact dir: {}", e))?;
let mut extracted = Vec::new();
for artifact in artifact_paths {
let mut cmd = Command::new(&self.docker_cmd);
cmd.arg("create");
cmd.arg("--name");
cmd.arg(format!("extract_{}", artifact.replace('/', "_")));
cmd.arg(image_tag);
let create_result = cmd
.output()
.map_err(|e| format!("Failed to create container: {}", e))?;
if create_result.status.success() {
let container_name = format!("extract_{}", artifact.replace('/', "_"));
let dest_path = self
.artifact_dir
.join(Path::new(artifact).file_name().unwrap_or_default());
let mut cp_cmd = Command::new(&self.docker_cmd);
cp_cmd.arg("cp");
cp_cmd.arg(format!("{}:{}", container_name, artifact));
cp_cmd.arg(&dest_path);
cp_cmd
.output()
.map_err(|e| format!("Failed to copy artifact: {}", e))?;
extracted.push(dest_path);
Command::new(&self.docker_cmd)
.args(&["rm", "-f", &container_name])
.output()
.ok();
}
}
Ok(extracted)
}
pub fn default_build_spec(project_name: &str) -> X86DockerfileSpec {
let (base_image, packages): (&str, Vec<&str>) = match project_name {
"postgresql" => (
"ubuntu:22.04",
vec![
"build-essential",
"libreadline-dev",
"zlib1g-dev",
"libssl-dev",
"libicu-dev",
"pkg-config",
"bison",
"flex",
],
),
"mysql" => (
"ubuntu:22.04",
vec![
"build-essential",
"cmake",
"libssl-dev",
"libncurses5-dev",
"pkg-config",
"bison",
],
),
"mongodb" => (
"ubuntu:22.04",
vec!["build-essential", "scons", "python3", "libssl-dev"],
),
"redis" => ("alpine:3.19", vec!["build-base", "linux-headers"]),
"memcached" => ("ubuntu:22.04", vec!["build-essential", "libevent-dev"]),
"nginx" => (
"ubuntu:22.04",
vec![
"build-essential",
"libpcre3-dev",
"zlib1g-dev",
"libssl-dev",
],
),
"httpd" => (
"ubuntu:22.04",
vec![
"build-essential",
"libssl-dev",
"libnghttp2-dev",
"libapr1-dev",
"libaprutil1-dev",
"pkg-config",
],
),
_ => ("ubuntu:22.04", vec!["build-essential", "pkg-config"]),
};
X86DockerfileSpec {
base_image: base_image.to_string(),
packages: packages.iter().map(|s| s.to_string()).collect(),
env_vars: vec![],
build_commands: vec![
format!("./configure --prefix=/usr/local"),
"make -j$(nproc)".into(),
"make install".into(),
],
copy_instructions: vec![],
entrypoint: None,
ports: vec![],
}
}
pub fn minimal_runtime_spec(project_name: &str) -> X86DockerfileSpec {
let mut spec = X86DockerfileSpec {
base_image: "ubuntu:22.04".into(),
packages: vec!["ca-certificates".into()],
env_vars: vec![],
build_commands: vec![],
copy_instructions: vec![],
entrypoint: None,
ports: vec![],
};
match project_name {
"postgresql" => {
spec.packages.extend(vec![
"libreadline8".into(),
"zlib1g".into(),
"libssl3".into(),
]);
}
"nginx" => {
spec.packages
.extend(vec!["libpcre3".into(), "zlib1g".into(), "libssl3".into()]);
}
_ => {}
}
spec
}
}
#[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_extra_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_version_new() {
let v = ExtraProjectVersion::new(1, 2, 3);
assert_eq!(v.major, 1);
assert_eq!(v.minor, 2);
assert_eq!(v.patch, 3);
}
#[test]
fn test_version_parse() {
let v = ExtraProjectVersion::parse("16.3.0").unwrap();
assert_eq!(v.major, 16);
assert_eq!(v.minor, 3);
assert_eq!(v.patch, 0);
}
#[test]
fn test_version_to_string() {
let v = ExtraProjectVersion::new(3, 12, 3);
assert_eq!(v.to_string(), "3.12.3");
}
#[test]
fn test_category_as_str() {
assert_eq!(ExtraProjectCategory::Database.as_str(), "database");
assert_eq!(ExtraProjectCategory::WebServer.as_str(), "web-server");
assert_eq!(
ExtraProjectCategory::LanguageRuntime.as_str(),
"language-runtime"
);
assert_eq!(ExtraProjectCategory::Browser.as_str(), "browser");
assert_eq!(ExtraProjectCategory::Cache.as_str(), "cache");
}
#[test]
fn test_category_display() {
let cat = ExtraProjectCategory::Database;
assert!(format!("{}", cat).contains("database"));
}
#[test]
fn test_projects_extra_new() {
let dir = create_temp_dir();
let extra = X86ProjectsExtra::new(&dir);
assert!(extra.configs.is_empty());
assert!(extra.list_projects().is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_register_single_project() {
let dir = create_temp_dir();
create_file(
&dir.join("src").join("server.c"),
"int main() { return 0; }",
);
let mut extra = X86ProjectsExtra::new(&dir);
extra.register(
Box::new(X86PostgreSQLConfig::default()),
ExtraProjectCategory::Database,
ExtraProjectVersion::new(16, 3, 0),
);
assert!(extra.configs.contains_key("postgresql"));
assert_eq!(
extra.category("postgresql"),
Some(ExtraProjectCategory::Database)
);
cleanup_temp_dir(&dir);
}
#[test]
fn test_register_all_projects() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
assert_eq!(extra.configs.len(), 15);
assert_eq!(
extra.category("postgresql"),
Some(ExtraProjectCategory::Database)
);
assert_eq!(
extra.category("nginx-extra"),
Some(ExtraProjectCategory::WebServer)
);
assert_eq!(
extra.category("cpython"),
Some(ExtraProjectCategory::LanguageRuntime)
);
assert_eq!(extra.category("v8"), Some(ExtraProjectCategory::Browser));
cleanup_temp_dir(&dir);
}
#[test]
fn test_list_projects_sorted() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
let names = extra.list_projects();
assert_eq!(names.len(), 15);
for i in 1..names.len() {
assert!(names[i - 1] <= names[i]);
}
cleanup_temp_dir(&dir);
}
#[test]
fn test_list_by_category() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
let dbs = extra.list_by_category(ExtraProjectCategory::Database);
assert_eq!(dbs.len(), 3);
assert!(dbs.contains(&"mongodb".to_string()));
assert!(dbs.contains(&"mysql".to_string()));
assert!(dbs.contains(&"postgresql".to_string()));
let browsers = extra.list_by_category(ExtraProjectCategory::Browser);
assert_eq!(browsers.len(), 4);
cleanup_temp_dir(&dir);
}
#[test]
fn test_version_info() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
let ver = extra.version("ruby").unwrap();
assert_eq!(ver.major, 3);
assert_eq!(ver.minor, 3);
assert_eq!(ver.patch, 1);
cleanup_temp_dir(&dir);
}
#[test]
fn test_generate_extended_report() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
let report = extra.generate_extended_report();
assert!(report.contains("X86 Extended Projects Build Report"));
assert!(report.contains("postgresql"));
assert!(report.contains("chromium"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_extra_project_names() {
assert_eq!(EXTRA_PROJECT_NAMES.len(), 15);
}
#[test]
fn test_postgresql_config_default() {
let config = X86PostgreSQLConfig::default();
assert_eq!(config.name(), "postgresql");
assert!(config.with_ssl);
assert!(config.with_llvm);
assert!(config.with_icu);
assert_eq!(config.blocksize, 8192);
assert_eq!(config.wal_segsize, 16);
}
#[test]
fn test_postgresql_defines() {
let config = X86PostgreSQLConfig::default();
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "USE_LLVM"));
assert!(defs.iter().any(|(k, _)| k == "USE_OPENSSL"));
}
#[test]
fn test_postgresql_build_system() {
let config = X86PostgreSQLConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Autotools);
}
#[test]
fn test_postgresql_no_panic_on_missing() {
let dir = create_temp_dir();
let config = X86PostgreSQLConfig::default();
let sources = config.sources(&dir);
let _ = sources;
let includes = config.includes(&dir);
let _ = includes;
cleanup_temp_dir(&dir);
}
#[test]
fn test_mysql_config_default() {
let config = X86MySQLConfig::default();
assert_eq!(config.name(), "mysql");
assert!(config.with_innodb);
assert!(config.with_ssl);
assert_eq!(config.default_charset, "utf8mb4");
}
#[test]
fn test_mysql_build_system() {
let config = X86MySQLConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::CMake);
}
#[test]
fn test_mysql_storage_engines() {
let config = X86MySQLConfig::default();
assert!(config.storage_engines.contains(&"InnoDB".to_string()));
assert!(config.storage_engines.contains(&"MyISAM".to_string()));
}
#[test]
fn test_mongodb_config_default() {
let config = X86MongoDBConfig::default();
assert_eq!(config.name(), "mongodb");
assert!(config.with_wiredtiger);
assert!(config.with_ssl);
}
#[test]
fn test_mongodb_build_system() {
let config = X86MongoDBConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::SCons);
}
#[test]
fn test_redis_extra_config_default() {
let config = X86RedisExtraConfig::default();
assert_eq!(config.name(), "redis-extra");
assert!(config.with_jemalloc);
assert!(config.enable_lto);
}
#[test]
fn test_redis_extra_malloc_type() {
let config = X86RedisExtraConfig::default();
assert!(matches!(config.malloc, X86ExtraMallocType::Jemalloc));
}
#[test]
fn test_memcached_config_default() {
let config = X86MemcachedConfig::default();
assert_eq!(config.name(), "memcached");
assert_eq!(config.port, 11211);
assert_eq!(config.threads, 4);
}
#[test]
fn test_memcached_build_system() {
let config = X86MemcachedConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Autotools);
}
#[test]
fn test_nginx_extra_config_default() {
let config = X86NginxExtraConfig::default();
assert_eq!(config.name(), "nginx-extra");
assert!(config.with_http_v2);
assert!(config.with_stream);
assert!(!config.with_mail);
}
#[test]
fn test_nginx_extra_build_system() {
let config = X86NginxExtraConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Autotools);
}
#[test]
fn test_httpd_config_default() {
let config = X86ApacheHttpdConfig::default();
assert_eq!(config.name(), "httpd");
assert!(config.with_ssl);
assert!(config.with_http2);
}
#[test]
fn test_httpd_mpm() {
let config = X86ApacheHttpdConfig::default();
assert_eq!(config.mpm.as_str(), "event");
}
#[test]
fn test_httpd_modules_list() {
let config = X86ApacheHttpdConfig::default();
assert!(!config.modules.is_empty());
assert!(config.modules.contains(&"ssl".to_string()));
}
#[test]
fn test_nodejs_config_default() {
let config = X86NodeJsConfig::default();
assert_eq!(config.name(), "node");
assert!(config.use_bundled_deps);
assert!(config.with_intl);
}
#[test]
fn test_nodejs_no_panic() {
let dir = create_temp_dir();
let config = X86NodeJsConfig::default();
let _ = config.sources(&dir);
let _ = config.includes(&dir);
cleanup_temp_dir(&dir);
}
#[test]
fn test_cpython_config_default() {
let config = X86CPythonConfig::default();
assert_eq!(config.name(), "cpython");
assert!(config.with_lto);
assert!(config.with_computed_gotos);
}
#[test]
fn test_cpython_extensions() {
let config = X86CPythonConfig::default();
assert!(config.extensions.contains(&"_ssl".to_string()));
assert!(config.extensions.contains(&"_sqlite3".to_string()));
}
#[test]
fn test_cpython_build_system() {
let config = X86CPythonConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Autotools);
}
#[test]
fn test_ruby_config_default() {
let config = X86RubyConfig::default();
assert_eq!(config.name(), "ruby");
assert!(config.with_yjit);
assert!(config.with_jemalloc);
assert_eq!(config.ruby_version, "3.3.0");
}
#[test]
fn test_ruby_yjit_defines() {
let config = X86RubyConfig::default();
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "YJIT_SUPPORT"));
}
#[test]
fn test_php_config_default() {
let config = X86PhpConfig::default();
assert_eq!(config.name(), "php");
assert!(config.with_jit);
assert!(config.with_opcache);
}
#[test]
fn test_php_zend_jit_define() {
let config = X86PhpConfig::default();
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "ZEND_JIT"));
}
#[test]
fn test_php_build_system() {
let config = X86PhpConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Autotools);
}
#[test]
fn test_v8_config_default() {
let config = X86V8Config::default();
assert_eq!(config.name(), "v8");
assert!(config.with_turbofan);
assert!(config.with_maglev);
assert!(config.with_wasm);
}
#[test]
fn test_v8_build_system() {
let config = X86V8Config::default();
assert_eq!(config.build_system(), X86BuildSystem::Ninja);
}
#[test]
fn test_v8_defines() {
let config = X86V8Config::default();
let defs = config.defines();
assert!(defs.iter().any(|(k, _)| k == "V8_TARGET_ARCH_X64"));
assert!(defs.iter().any(|(k, _)| k == "V8_COMPRESS_POINTERS"));
}
#[test]
fn test_spidermonkey_config_default() {
let config = X86SpiderMonkeyConfig::default();
assert_eq!(config.name(), "spidermonkey");
assert!(config.with_ion);
assert!(config.with_baseline);
}
#[test]
fn test_spidermonkey_build_system() {
let config = X86SpiderMonkeyConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Autotools);
}
#[test]
fn test_webkit_config_default() {
let config = X86WebKitConfig::default();
assert_eq!(config.name(), "webkit");
assert!(config.with_jsc);
assert!(config.with_ftl_jit);
}
#[test]
fn test_webkit_build_system() {
let config = X86WebKitConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::CMake);
}
#[test]
fn test_webkit_jit_tier() {
assert_eq!(X86JscJitTier::All.as_str(), "all");
assert_eq!(X86JscJitTier::FTL.as_str(), "ftl");
assert_eq!(X86JscJitTier::DFG.as_str(), "dfg");
}
#[test]
fn test_webkit_port() {
assert_eq!(X86WebKitPort::Gtk.as_str(), "GTK");
assert_eq!(X86WebKitPort::JscOnly.as_str(), "JSCOnly");
}
#[test]
fn test_chromium_config_default() {
let config = X86ChromiumConfig::default();
assert_eq!(config.name(), "chromium");
assert!(config.with_blink);
assert!(config.with_v8);
assert!(config.with_skia);
}
#[test]
fn test_chromium_build_system() {
let config = X86ChromiumConfig::default();
assert_eq!(config.build_system(), X86BuildSystem::Ninja);
}
#[test]
fn test_chromium_build_type_flags() {
let mut config = X86ChromiumConfig::default();
config.build_type = X86ChromiumBuildType::Official;
let flags = config.compiler_flags();
assert!(flags.iter().any(|f| f.contains("flto")));
}
#[test]
fn test_compare_new() {
let dir = create_temp_dir();
let compare = X86ProjectCompare::new(&dir);
assert!(compare.clang_results.is_none());
assert!(compare.gcc_results.is_none());
assert_eq!(compare.benchmark_iterations, 5);
cleanup_temp_dir(&dir);
}
#[test]
fn test_compare_with_compilers() {
let dir = create_temp_dir();
let compare = X86ProjectCompare::new(&dir)
.with_compilers(Path::new("/usr/bin/clang"), Path::new("/usr/bin/gcc"));
assert_eq!(compare.clang_path, PathBuf::from("/usr/bin/clang"));
assert_eq!(compare.gcc_path, PathBuf::from("/usr/bin/gcc"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_compare_with_iterations() {
let dir = create_temp_dir();
let compare = X86ProjectCompare::new(&dir).with_iterations(10);
assert_eq!(compare.benchmark_iterations, 10);
cleanup_temp_dir(&dir);
}
#[test]
fn test_compare_generate_summary_empty() {
let dir = create_temp_dir();
let mut compare = X86ProjectCompare::new(&dir);
let summary = compare.generate_summary();
assert_eq!(summary.build_time_ratio, 0.0);
assert_eq!(summary.binary_size_ratio, 0.0);
cleanup_temp_dir(&dir);
}
#[test]
fn test_compare_generate_summary_with_data() {
let dir = create_temp_dir();
let mut compare = X86ProjectCompare::new(&dir);
compare.clang_results = Some(X86CompareResult {
compiler: "clang".into(),
build_time: Duration::from_millis(100),
binary_size: Some(500000),
stripped_size: None,
sources_compiled: 10,
build_errors: 0,
build_warnings: 2,
test_results: None,
performance: None,
flags: vec!["-O2".into()],
});
compare.gcc_results = Some(X86CompareResult {
compiler: "gcc".into(),
build_time: Duration::from_millis(150),
binary_size: Some(550000),
stripped_size: None,
sources_compiled: 10,
build_errors: 0,
build_warnings: 1,
test_results: None,
performance: None,
flags: vec!["-O2".into()],
});
compare.comparison = Some(compare.generate_summary());
let report = compare.print_report();
assert!(report.contains("Clang vs GCC"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_performance_metrics() {
let metrics = X86PerformanceMetrics {
wall_time: Duration::from_secs(1),
cpu_time: Duration::from_millis(900),
peak_memory: 1024 * 1024,
iterations_per_sec: 123.4,
samples: 5,
raw_timings_ms: vec![10.0, 11.0, 12.0, 10.5, 11.5],
};
assert_eq!(metrics.samples, 5);
assert!((metrics.iterations_per_sec - 123.4).abs() < 0.01);
}
#[test]
fn test_patch_manager_new() {
let dir = create_temp_dir();
let pm = X86ProjectPatch::new(&dir);
assert!(pm.patches.is_empty());
assert!(pm.queue.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_generate_and_apply_patch() {
let dir = create_temp_dir();
let original_file = dir.join("test.c");
create_file(&original_file, "int main() {\n return 0;\n}\n");
let mut pm = X86ProjectPatch::new(&dir);
let patch = pm
.generate_patch(
"test-project",
"fix-return",
"Fix return value",
&original_file,
"int main() {\n return 1;\n}\n",
)
.unwrap();
assert_eq!(patch.name, "fix-return");
assert!(!patch.applied);
pm.apply_patch("fix-return").unwrap();
let applied: Vec<&X86Patch> = pm.list_applied();
assert_eq!(applied.len(), 1);
assert_eq!(applied[0].name, "fix-return");
let content = fs::read_to_string(&original_file).unwrap();
assert!(content.contains("return 1"));
pm.revert_patch("fix-return").unwrap();
let applied: Vec<&X86Patch> = pm.list_applied();
assert_eq!(applied.len(), 0);
cleanup_temp_dir(&dir);
}
#[test]
fn test_patch_enqueue_and_process() {
let dir = create_temp_dir();
let original_file = dir.join("test.c");
create_file(&original_file, "int main() { return 0; }");
let mut pm = X86ProjectPatch::new(&dir);
pm.generate_patch(
"test",
"patch1",
"First patch",
&original_file,
"int main() { return 1; }",
)
.unwrap();
pm.enqueue("patch1");
assert_eq!(pm.queue.len(), 1);
let results = pm.process_queue();
assert_eq!(results.len(), 1);
assert!(results[0].is_ok());
assert!(pm.queue.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_patch_list_pending() {
let dir = create_temp_dir();
let original_file = dir.join("test.c");
create_file(&original_file, "int main() { return 0; }");
let mut pm = X86ProjectPatch::new(&dir);
pm.generate_patch("test", "p1", "Patch 1", &original_file, "int x;\n")
.unwrap();
pm.generate_patch("test", "p2", "Patch 2", &original_file, "int y;\n")
.unwrap();
let pending = pm.list_pending();
assert_eq!(pending.len(), 2);
pm.apply_patch("p1").unwrap();
let pending = pm.list_pending();
assert_eq!(pending.len(), 1);
cleanup_temp_dir(&dir);
}
#[test]
fn test_patch_history() {
let dir = create_temp_dir();
let original_file = dir.join("test.c");
create_file(&original_file, "int main() { return 0; }");
let mut pm = X86ProjectPatch::new(&dir);
pm.generate_patch("test", "hist-patch", "Test", &original_file, "int x;\n")
.unwrap();
pm.apply_patch("hist-patch").unwrap();
pm.revert_patch("hist-patch").unwrap();
let history = pm.get_history();
assert_eq!(history.len(), 3);
cleanup_temp_dir(&dir);
}
#[test]
fn test_patch_already_applied_error() {
let dir = create_temp_dir();
let original_file = dir.join("test.c");
create_file(&original_file, "int main() { return 0; }");
let mut pm = X86ProjectPatch::new(&dir);
pm.generate_patch("test", "dup", "Dup", &original_file, "int x;\n")
.unwrap();
pm.apply_patch("dup").unwrap();
let result = pm.apply_patch("dup");
assert!(result.is_err());
cleanup_temp_dir(&dir);
}
#[test]
fn test_patch_format() {
assert_eq!(X86PatchFormat::Unified.as_str(), "unified");
assert_eq!(X86PatchFormat::Context.as_str(), "context");
assert_eq!(X86PatchFormat::Git.as_str(), "git");
}
#[test]
fn test_container_manager_new() {
let dir = create_temp_dir();
let cm = X86ProjectContainer::new(&dir);
assert!(cm.use_buildkit);
assert!(cm.build_results.is_empty());
cleanup_temp_dir(&dir);
}
#[test]
fn test_generate_dockerfile() {
let dir = create_temp_dir();
let cm = X86ProjectContainer::new(&dir);
let config = X86PostgreSQLConfig::default();
let spec = X86DockerfileSpec {
base_image: "ubuntu:22.04".into(),
packages: vec!["build-essential".into(), "libssl-dev".into()],
env_vars: vec![("PG_VERSION".into(), "16.3".into())],
build_commands: vec!["./configure".into(), "make".into()],
copy_instructions: vec![],
entrypoint: Some("/usr/local/bin/postgres".into()),
ports: vec![5432],
};
let path = cm
.generate_dockerfile("postgresql", &config, &spec)
.unwrap();
assert!(path.exists());
let content = fs::read_to_string(&path).unwrap();
assert!(content.contains("FROM ubuntu:22.04"));
assert!(content.contains("PG_VERSION=16.3"));
assert!(content.contains("./configure"));
assert!(content.contains("EXPOSE 5432"));
assert!(content.contains("ENTRYPOINT"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_generate_multi_stage_dockerfile() {
let dir = create_temp_dir();
let cm = X86ProjectContainer::new(&dir);
let build_spec = X86DockerfileSpec {
base_image: "ubuntu:22.04".into(),
packages: vec!["build-essential".into()],
env_vars: vec![],
build_commands: vec!["make".into()],
copy_instructions: vec![],
entrypoint: None,
ports: vec![],
};
let runtime_spec = X86DockerfileSpec {
base_image: "alpine:3.19".into(),
packages: vec![],
env_vars: vec![],
build_commands: vec![],
copy_instructions: vec![],
entrypoint: Some("/app/binary".into()),
ports: vec![],
};
let path = cm
.generate_multi_stage_dockerfile(
"myapp",
&build_spec,
&runtime_spec,
&["/app/binary", "/app/config"],
)
.unwrap();
assert!(path.exists());
let content = fs::read_to_string(&path).unwrap();
assert!(content.contains("AS builder"));
assert!(content.contains("FROM alpine:3.19"));
assert!(content.contains("COPY --from=builder"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_default_build_spec() {
let spec = X86ProjectContainer::default_build_spec("postgresql");
assert_eq!(spec.base_image, "ubuntu:22.04");
assert!(!spec.packages.is_empty());
}
#[test]
fn test_default_build_spec_all_projects() {
for name in EXTRA_PROJECT_NAMES {
let spec = X86ProjectContainer::default_build_spec(name);
assert!(!spec.base_image.is_empty());
}
}
#[test]
fn test_minimal_runtime_spec() {
let spec = X86ProjectContainer::minimal_runtime_spec("postgresql");
assert!(spec.packages.contains(&"libssl3".to_string()));
let spec = X86ProjectContainer::minimal_runtime_spec("nginx");
assert!(spec.packages.contains(&"libpcre3".to_string()));
}
#[test]
fn test_all_configs_implement_trait() {
let dir = create_temp_dir();
let configs: Vec<Box<dyn X86ProjectConfigurable>> = vec![
Box::new(X86PostgreSQLConfig::default()),
Box::new(X86MySQLConfig::default()),
Box::new(X86MongoDBConfig::default()),
Box::new(X86RedisExtraConfig::default()),
Box::new(X86MemcachedConfig::default()),
Box::new(X86NginxExtraConfig::default()),
Box::new(X86ApacheHttpdConfig::default()),
Box::new(X86NodeJsConfig::default()),
Box::new(X86CPythonConfig::default()),
Box::new(X86RubyConfig::default()),
Box::new(X86PhpConfig::default()),
Box::new(X86V8Config::default()),
Box::new(X86SpiderMonkeyConfig::default()),
Box::new(X86WebKitConfig::default()),
Box::new(X86ChromiumConfig::default()),
];
for config in &configs {
let name = config.name();
assert!(!name.is_empty(), "Config has empty name");
let defs = config.defines();
assert!(!defs.is_empty(), "{} has empty defines", name);
let includes = config.includes(&dir);
let _ = includes;
let flags = config.compiler_flags();
assert!(!flags.is_empty(), "{} has empty compiler flags", name);
let bs = config.build_system();
let _ = bs;
let sources = config.sources(&dir);
let _ = sources;
let lflags = config.linker_flags();
let _ = lflags;
let libs = config.libraries();
let _ = libs;
let tests = config.test_commands(&dir);
let _ = tests;
let notes = config.notes();
let _ = notes;
}
cleanup_temp_dir(&dir);
}
#[test]
fn test_no_panic_on_empty_project() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
let result = extra.build_one("postgresql");
let _ = result;
cleanup_temp_dir(&dir);
}
#[test]
fn test_extended_report_with_history() {
let dir = create_temp_dir();
let mut extra = X86ProjectsExtra::new(&dir);
extra.register_all();
extra.build_history.push(ExtraBuildRecord {
project_name: "test".into(),
timestamp: SystemTime::now(),
duration: Duration::from_millis(500),
success: true,
compiler_used: Some("clang".into()),
binary_size: Some(1024),
error_count: 0,
warning_count: 1,
});
let report = extra.generate_extended_report();
assert!(report.contains("Build History"));
assert!(report.contains("test"));
cleanup_temp_dir(&dir);
}
#[test]
fn test_compare_binary_sizes() {
let dir = create_temp_dir();
let mut compare = X86ProjectCompare::new(&dir);
create_file(
&dir.join("binary.clang"),
"mock binary content here for clang",
);
create_file(
&dir.join("binary.gcc"),
"mock binary content here for gcc longer",
);
let result = compare.compare_binary_sizes(&dir.join("binary"));
assert!(result.is_ok());
cleanup_temp_dir(&dir);
}
#[test]
fn test_build_record_clone() {
let record = ExtraBuildRecord {
project_name: "p".into(),
timestamp: SystemTime::now(),
duration: Duration::from_secs(1),
success: true,
compiler_used: None,
binary_size: None,
error_count: 0,
warning_count: 0,
};
let _cloned = record.clone();
assert_eq!(_cloned.project_name, "p");
}
#[test]
fn test_compare_result_clone() {
let result = X86CompareResult {
compiler: "c".into(),
build_time: Duration::from_millis(100),
binary_size: Some(1000),
stripped_size: Some(500),
sources_compiled: 10,
build_errors: 0,
build_warnings: 2,
test_results: None,
performance: Some(X86PerformanceMetrics {
wall_time: Duration::from_secs(1),
cpu_time: Duration::from_millis(900),
peak_memory: 1024,
iterations_per_sec: 100.0,
samples: 5,
raw_timings_ms: vec![10.0, 11.0],
}),
flags: vec!["-O2".into()],
};
let _cloned = result.clone();
assert_eq!(_cloned.compiler, "c");
}
#[test]
fn test_comparison_summary_clone() {
let summary = X86ComparisonSummary {
build_time_winner: "clang".into(),
build_time_ratio: 0.8,
binary_size_winner: "clang".into(),
binary_size_ratio: 0.95,
performance_winner: Some("clang".into()),
performance_ratio: Some(1.2),
correctness_equal: true,
clang_pass_rate: 99.0,
gcc_pass_rate: 98.0,
details: vec!["detail1".into()],
};
let _cloned = summary.clone();
assert_eq!(_cloned.build_time_winner, "clang");
}
#[test]
fn test_patch_clone() {
let patch = X86Patch {
name: "p1".into(),
description: "desc".into(),
project: "proj".into(),
content: "--- a\n+++ b\n".into(),
format: X86PatchFormat::Unified,
affected_files: vec!["f1.c".into()],
applied: false,
revertible: true,
created_at: SystemTime::now(),
compressed: false,
};
let _cloned = patch.clone();
assert_eq!(_cloned.name, "p1");
}
#[test]
fn test_container_build_result_clone() {
let result = X86ContainerBuildResult {
image_id: "img123".into(),
build_duration: Duration::from_secs(30),
exit_code: 0,
stdout: "ok".into(),
stderr: String::new(),
};
let _cloned = result.clone();
assert_eq!(_cloned.image_id, "img123");
}
#[test]
fn test_all_enum_displays() {
for cat in &[
ExtraProjectCategory::Database,
ExtraProjectCategory::WebServer,
ExtraProjectCategory::LanguageRuntime,
ExtraProjectCategory::Browser,
ExtraProjectCategory::Cache,
] {
assert!(!cat.as_str().is_empty());
}
for sys in &[
X86MysqlSslType::OpenSSL,
X86MysqlSslType::WolfSSL,
X86MysqlSslType::None,
] {
let _ = sys;
}
}
#[test]
fn test_v8_opt_level_variants() {
let variants = [
X86V8OptLevel::Debug,
X86V8OptLevel::Release,
X86V8OptLevel::OptDebug,
X86V8OptLevel::Size,
];
for v in &variants {
let mut config = X86V8Config::default();
config.opt_level = *v;
let flags = config.compiler_flags();
let _ = flags;
}
}
#[test]
fn test_chromium_branding_variants() {
let variants = [X86ChromiumBranding::Chrome, X86ChromiumBranding::Chromium];
assert_eq!(variants.len(), 2);
}
#[test]
fn test_apache_mpm_all_variants() {
let mpms = [
X86ApacheMpm::Event,
X86ApacheMpm::Worker,
X86ApacheMpm::Prefork,
X86ApacheMpm::Simple,
X86ApacheMpm::Motorz,
];
for mpm in &mpms {
assert!(!mpm.as_str().is_empty());
}
}
#[test]
fn test_mongo_link_model_variants() {
let models = [
X86MongoLinkModel::Dynamic,
X86MongoLinkModel::Static,
X86MongoLinkModel::Auto,
];
assert_eq!(models.len(), 3);
}
}