Files
core/test/rust/performance/build/mod.rs
T
2026-11-03 03:32:07 +00:00

187 lines
7.6 KiB
Rust

//! 所有性能组共用正式生成器、锁定依赖和 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::<std::collections::BTreeSet<_>>();
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();
}
}