//! 所有性能组共用正式生成器、锁定依赖和 release 构建参数。 use crate::auth_build; mod collections; pub(crate) use collections::run as collections; use dever::compiler::{emit, project::Project}; use dever::process::{child::output::require_success, command}; use std::path::Path; pub(crate) fn compare( core: &Path, fixture: &str, name: &str, reference: &str, functions: &[(&str, &str)], definitions: &[(&str, &str)], ) { let path = core.join(format!("test/rust/fixtures/{fixture}")); let path = if path.is_dir() { path } else { path.with_extension("dever") }; let project = Project::load(&path).unwrap(); let program = project.check().unwrap(); let mut comparison = auth_build::metadata(name, &program, reference); if name == "decimal-extra" { for (marker, path) in [ ("DECIMAL_CODEC_SOURCE", "codec/numeric.rs"), ("DECIMAL_SCALAR_SOURCE", "scalar/decimal.rs"), ] { comparison = comparison.replace( &format!("\"{marker}\""), &format!("{:?}", core.join("rust/src/runtime/database").join(path)), ); } } let memory = std::env::args().any(|argument| argument == "--memory"); if memory { assert!( comparison.contains("fn compare_output<"), "memory requires the shared sample harness" ); comparison = comparison.replacen("fn compare_output<", "fn timed_compare_output<", 1); comparison.push_str(include_str!("../memory.rs")); } let mut roots = Vec::new(); for (marker, name) in functions { if let Some(index) = program .commands .iter() .position(|command| command.name == *name) { comparison = comparison.replace(marker, &format!("command{index}")); roots.push(program.commands[index].function); continue; } let (package, name) = name .rsplit_once('.') .map_or((None, *name), |(package, name)| (Some(package), name)); let position = program .functions .iter() .position(|function| { function.signature.name == name && package.is_none_or(|package| { program.packages[function.signature.source] == package }) }) .unwrap(); comparison = comparison.replace(marker, &format!("function{position}")); roots.push(position); } for (marker, name) in definitions { let position = program .definitions .iter() .position(|definition| definition.name == *name) .unwrap(); comparison = comparison.replace(marker, &format!("Type{position}")); } let task_dependencies = if name == "rest-json" { "chrono = { version = \"=0.4.45\", default-features = false, features = [\"std\"] }\n" } else if name == "tasks" { "tokio = { version = \"=1.53.1\", features = [\"rt-multi-thread\", \"sync\", \"time\", \"macros\"] }\ntokio-util = { version = \"=0.7.19\", features = [\"rt\"] }\nfutures-util = { version = \"=0.3.34\", default-features = false, features = [\"std\"] }\n" } else if matches!(name, "websocket" | "websocket-protocol") { "tokio = { version = \"=1.53.1\", features = [\"rt-multi-thread\", \"net\", \"sync\", \"time\", \"macros\"] }\ntokio-util = { version = \"=0.7.19\", features = [\"rt\"] }\nfutures-util = { version = \"=0.3.34\", default-features = false, features = [\"std\", \"sink\"] }\ntokio-tungstenite = { version = \"=0.30.0\", default-features = false, features = [\"handshake\"] }\n" } else if name == "query" { "axum = { version = \"=0.8.9\", default-features = false, features = [\"query\"] }\nserde = { version = \"=1.0.228\", features = [\"derive\"] }\nserde_urlencoded = \"=0.7.1\"\n" } else if name == "transport" { crate::transport::DEPENDENCIES } else if matches!(name, "auth" | "auth-http") { "tokio = { version = \"=1.53.1\", features = [\"sync\"] }\n" } else if name == "extended-primitives" { "sha2 = \"=0.10.9\"\nuuid = { version = \"=1.28.0\", default-features = false }\n" } else if name == "decimal-extra" { "serde = \"=1.0.228\"\nserde_json = \"=1.0.145\"\nbytes = \"=1.12.1\"\n" } else if name == "api-wire" { "serde_json = \"=1.0.145\"\nhyper = \"=1.11.1\"\n" } else if matches!(name, "database" | "database-postgres" | "application") { "serde_json = \"=1.0.145\"\n" } else { "" }; let name = if memory { format!("{name}-memory") } else { name.to_owned() }; let directory = core.join(format!("rust/target/performance/{name}")); std::fs::create_dir_all(&directory).unwrap(); roots.extend(program.commands.iter().map(|command| command.function)); if matches!( name.trim_end_matches("-memory"), "auth" | "auth-http" | "jobs" | "api-wire" | "rest-wire" | "rest-json" ) { roots.extend(program.entries()); } let roots = roots.into_iter().collect::>(); let functions = program.reachable(roots.iter().copied()); let mut source = emit::generate(&program, &project.sources, &roots); source.push_str(&auth_build::application( name.trim_end_matches("-memory"), &program, )); source.push_str(&comparison); write(&directory.join("main.rs"), &source); let mut features = dever::compiler::build::runtime_features(&program, &functions); if name.trim_end_matches("-memory") == "query" { features.push("api"); } if matches!( name.trim_end_matches("-memory"), "database-postgres" | "decimal-extra" ) { features.push("postgres"); } let features = format!(", features = {features:?}"); write( &directory.join("Cargo.toml"), &format!( "[package]\nname = \"dever-comparison-{name}\"\nversion = \"0.0.1\"\nedition = \"2024\"\n[workspace]\n[[bin]]\nname = \"dever-comparison-{name}\"\npath = \"main.rs\"\n[dependencies]\ndever-runtime = {{ path = {:?}{features} }}\nindexmap = \"=2.14.0\"\ndec = \"=0.4.11\"\n{task_dependencies}[profile.release]\nlto = \"fat\"\ncodegen-units = 1\noverflow-checks = true\n", core.join("rust/src/runtime").to_string_lossy() ), ); // 基准清单随候选运行库变化;只刷新这个生成项目的离线依赖锁。 let arguments = ["+1.95.0", "generate-lockfile", "--offline"]; let child = command::start("cargo".as_ref(), &arguments, &directory, true).unwrap(); require_success(child.output().unwrap()).unwrap(); let result = command::start( "cargo".as_ref(), &[ "+1.95.0", "build", "--offline", "--locked", "--release", "--target-dir", "../../programs", ], &directory, true, ) .unwrap() .output() .unwrap(); require_success(result).unwrap(); let binary = core.join(format!( "rust/target/programs/release/dever-comparison-{name}" )); if memory || std::env::args().any(|argument| argument == "--build-only") { println!("{}", binary.display()); return; } let status = command::start(binary.as_os_str(), &[] as &[&str], &directory, false) .unwrap() .wait() .unwrap(); assert!(status.success()); } fn write(path: &Path, contents: &str) { if std::fs::read_to_string(path).ok().as_deref() != Some(contents) { std::fs::write(path, contents).unwrap(); } }