summaryrefslogtreecommitdiff
path: root/build.rs
diff options
context:
space:
mode:
Diffstat (limited to 'build.rs')
-rw-r--r--build.rs593
1 files changed, 42 insertions, 551 deletions
diff --git a/build.rs b/build.rs
index 07b5c10..ad02511 100644
--- a/build.rs
+++ b/build.rs
@@ -1,563 +1,54 @@
use std::env;
use std::path::PathBuf;
-use std::process::Command;
-use string_proc::pascal_case;
+use crate::r#gen::{
+ gen_commands_file::generate_commands_file, gen_compile_info::generate_compile_info,
+ gen_iscc_script::generate_installer_script, gen_mod_files::generate_collect_files,
+ gen_override_renderer::generate_override_renderer, gen_renderers_file::generate_renderers_file,
+ gen_specific_renderer::generate_specific_renderer,
+};
-const COMMANDS_PATH: &str = "./src/cmds/cmd/";
+pub mod r#gen;
-const COMPILE_INFO_RS_TEMPLATE: &str = "./templates/compile_info.rs.template";
-const COMPILE_INFO_RS: &str = "./src/data/compile_info.rs";
-
-const SETUP_JV_CLI_ISS_TEMPLATE: &str = "./templates/setup_jv_cli.iss.template";
-const SETUP_JV_CLI_ISS: &str = "./scripts/setup/windows/setup_jv_cli.iss";
-
-const REGISTRY_RS_TEMPLATE: &str = "./templates/_registry.rs.template";
-const REGISTRY_RS: &str = "./src/systems/cmd/_registry.rs";
-
-const RENDERER_LIST_TEMPLATE: &str = "./templates/_renderers.rs.template";
-const RENDERER_LIST: &str = "./src/systems/cmd/_renderers.rs";
-
-const REGISTRY_TOML: &str = "./.cargo/registry.toml";
-
-fn main() {
+#[tokio::main]
+async fn main() {
println!("cargo:rerun-if-env-changed=FORCE_BUILD");
- let repo_root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
-
- if cfg!(target_os = "windows") {
- // Only generate installer script on Windows
- if let Err(e) = generate_installer_script(&repo_root) {
- eprintln!("Failed to generate installer script: {}", e);
- std::process::exit(1);
- }
- }
-
- if let Err(e) = generate_compile_info(&repo_root) {
- eprintln!("Failed to generate compile info: {}", e);
- std::process::exit(1);
- }
-
- if let Err(e) = generate_cmd_registry_file(&repo_root) {
- eprintln!("Failed to generate registry file: {}", e);
- std::process::exit(1);
- }
-
- if let Err(e) = generate_renderer_list_file(&repo_root) {
- eprintln!("Failed to generate renderer list: {}", e);
- std::process::exit(1);
- }
-
- if let Err(e) = generate_collect_files(&repo_root) {
- eprintln!("Failed to generate collect files: {}", e);
- std::process::exit(1);
- }
-}
-
-/// Generate Inno Setup installer script (Windows only)
-fn generate_installer_script(repo_root: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
- let template_path = repo_root.join(SETUP_JV_CLI_ISS_TEMPLATE);
- let output_path = repo_root.join(SETUP_JV_CLI_ISS);
-
- let template = std::fs::read_to_string(&template_path)?;
-
- let author = get_author()?;
- let version = get_version();
- let site = get_site()?;
-
- let generated = template
- .replace("<<<AUTHOR>>>", &author)
- .replace("<<<VERSION>>>", &version)
- .replace("<<<SITE>>>", &site);
-
- std::fs::write(output_path, generated)?;
- Ok(())
-}
-
-fn get_author() -> Result<String, Box<dyn std::error::Error>> {
- let cargo_toml_path = std::path::Path::new("Cargo.toml");
- let cargo_toml_content = std::fs::read_to_string(cargo_toml_path)?;
- let cargo_toml: toml::Value = toml::from_str(&cargo_toml_content)?;
-
- if let Some(package) = cargo_toml.get("package") {
- if let Some(authors) = package.get("authors") {
- if let Some(authors_array) = authors.as_array() {
- if let Some(first_author) = authors_array.get(0) {
- if let Some(author_str) = first_author.as_str() {
- return Ok(author_str.to_string());
- }
- }
- }
- }
- }
-
- Err("Author not found in Cargo.toml".into())
-}
-
-fn get_site() -> Result<String, Box<dyn std::error::Error>> {
- let cargo_toml_path = std::path::Path::new("Cargo.toml");
- let cargo_toml_content = std::fs::read_to_string(cargo_toml_path)?;
- let cargo_toml: toml::Value = toml::from_str(&cargo_toml_content)?;
-
- if let Some(package) = cargo_toml.get("package") {
- if let Some(homepage) = package.get("homepage") {
- if let Some(site_str) = homepage.as_str() {
- return Ok(site_str.to_string());
- }
- }
- }
-
- Err("Homepage not found in Cargo.toml".into())
-}
-
-/// Generate compile info
-fn generate_compile_info(repo_root: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
- // Read the template code
- let template_code = std::fs::read_to_string(repo_root.join(COMPILE_INFO_RS_TEMPLATE))?;
-
- let date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
- let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());
- let platform = get_platform(&target);
- let toolchain = get_toolchain();
- let version = get_version();
- let branch = get_git_branch().unwrap_or_else(|_| "unknown".to_string());
- let commit = get_git_commit().unwrap_or_else(|_| "unknown".to_string());
-
- let generated_code = template_code
- .replace("{date}", &date)
- .replace("{target}", &target)
- .replace("{platform}", &platform)
- .replace("{toolchain}", &toolchain)
- .replace("{version}", &version)
- .replace("{branch}", &branch)
- .replace("{commit}", &commit);
-
- // Write the generated code
- let compile_info_path = repo_root.join(COMPILE_INFO_RS);
- std::fs::write(compile_info_path, generated_code)?;
-
- Ok(())
-}
-
-fn get_platform(target: &str) -> String {
- if target.contains("windows") {
- "Windows".to_string()
- } else if target.contains("linux") {
- "Linux".to_string()
- } else if target.contains("darwin") || target.contains("macos") {
- "macOS".to_string()
- } else if target.contains("android") {
- "Android".to_string()
- } else if target.contains("ios") {
- "iOS".to_string()
- } else {
- "Unknown".to_string()
- }
-}
-
-fn get_toolchain() -> String {
- let rustc_version = std::process::Command::new("rustc")
- .arg("--version")
- .output()
- .ok()
- .and_then(|output| String::from_utf8(output.stdout).ok())
- .unwrap_or_else(|| "unknown".to_string())
- .trim()
- .to_string();
-
- let channel = if rustc_version.contains("nightly") {
- "nightly"
- } else if rustc_version.contains("beta") {
- "beta"
- } else {
- "stable"
- };
-
- format!("{} ({})", rustc_version, channel)
-}
-
-fn get_version() -> String {
- let cargo_toml_path = std::path::Path::new("Cargo.toml");
- let cargo_toml_content = match std::fs::read_to_string(cargo_toml_path) {
- Ok(content) => content,
- Err(_) => return "unknown".to_string(),
- };
-
- let cargo_toml: toml::Value = match toml::from_str(&cargo_toml_content) {
- Ok(value) => value,
- Err(_) => return "unknown".to_string(),
- };
-
- if let Some(workspace) = cargo_toml.get("workspace") {
- if let Some(package) = workspace.get("package") {
- if let Some(version) = package.get("version") {
- if let Some(version_str) = version.as_str() {
- return version_str.to_string();
- }
- }
- }
- }
-
- "unknown".to_string()
-}
-
-/// Get current git branch
-fn get_git_branch() -> Result<String, Box<dyn std::error::Error>> {
- let output = Command::new("git")
- .args(["branch", "--show-current"])
- .output()?;
-
- if output.status.success() {
- let branch = String::from_utf8(output.stdout)?.trim().to_string();
-
- if branch.is_empty() {
- // Try to get HEAD reference if no branch (detached HEAD)
- let output = Command::new("git")
- .args(["rev-parse", "--abbrev-ref", "HEAD"])
- .output()?;
-
- if output.status.success() {
- let head_ref = String::from_utf8(output.stdout)?.trim().to_string();
- return Ok(head_ref);
- }
- } else {
- return Ok(branch);
- }
- }
-
- Err("Failed to get git branch".into())
-}
-
-/// Get current git commit hash
-fn get_git_commit() -> Result<String, Box<dyn std::error::Error>> {
- let output = Command::new("git").args(["rev-parse", "HEAD"]).output()?;
-
- if output.status.success() {
- let commit = String::from_utf8(output.stdout)?.trim().to_string();
- return Ok(commit);
- }
-
- Err("Failed to get git commit".into())
-}
-
-/// Generate registry file from Registry.toml configuration
-fn generate_cmd_registry_file(repo_root: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
- let template_path = repo_root.join(REGISTRY_RS_TEMPLATE);
- let output_path = repo_root.join(REGISTRY_RS);
- let config_path = repo_root.join(REGISTRY_TOML);
-
- // Read the template
- let template = std::fs::read_to_string(&template_path)?;
-
- // Read and parse the TOML configuration
- let config_content = std::fs::read_to_string(&config_path)?;
- let config: toml::Value = toml::from_str(&config_content)?;
-
- // Collect all command configurations
- let mut commands = Vec::new();
- let mut nodes = Vec::new();
-
- // First, collect commands from registry.toml
- if let Some(table) = config.as_table() {
- if let Some(cmd_table) = table.get("cmd") {
- if let Some(cmd_table) = cmd_table.as_table() {
- for (key, cmd_value) in cmd_table {
- if let Some(cmd_config) = cmd_value.as_table() {
- if let (Some(node), Some(cmd_type)) = (
- cmd_config.get("node").and_then(|v| v.as_str()),
- cmd_config.get("type").and_then(|v| v.as_str()),
- ) {
- let n = node.replace(".", " ");
- nodes.push(n.clone());
- commands.push((key.to_string(), n, cmd_type.to_string()));
- }
- }
+ let repo_root = std::sync::Arc::new(PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()));
+
+ let _ = tokio::join!(
+ tokio::spawn({
+ let repo_root = repo_root.clone();
+ async move { generate_compile_info(&repo_root).await }
+ }),
+ tokio::spawn({
+ let repo_root = repo_root.clone();
+ async move { generate_commands_file(&repo_root).await }
+ }),
+ tokio::spawn({
+ let repo_root = repo_root.clone();
+ async move { generate_renderers_file(&repo_root).await }
+ }),
+ tokio::spawn({
+ let repo_root = repo_root.clone();
+ async move { generate_collect_files(&repo_root).await }
+ }),
+ tokio::spawn({
+ let repo_root = repo_root.clone();
+ async move { generate_override_renderer(&repo_root).await }
+ }),
+ tokio::spawn({
+ let repo_root = repo_root.clone();
+ async move { generate_specific_renderer(&repo_root).await }
+ }),
+ tokio::spawn({
+ async move {
+ if cfg!(target_os = "windows") {
+ // Only generate installer script on Windows
+ let repo_root = repo_root.clone();
+ generate_installer_script(&repo_root).await
}
}
- }
- }
-
- // Then, automatically register commands from COMMANDS_PATH
- let commands_dir = repo_root.join(COMMANDS_PATH);
- if commands_dir.exists() && commands_dir.is_dir() {
- for entry in std::fs::read_dir(&commands_dir)? {
- let entry = entry?;
- let path = entry.path();
-
- if !path.is_file() {
- continue;
- }
-
- let extension = match path.extension() {
- Some(ext) => ext,
- None => continue,
- };
-
- if extension != "rs" {
- continue;
- }
-
- let file_name = match path.file_stem().and_then(|s| s.to_str()) {
- Some(name) => name,
- None => continue,
- };
-
- // Skip files that start with underscore
- if file_name.starts_with('_') {
- continue;
- }
-
- // Convert filename to PascalCase
- let pascal_name = pascal_case!(file_name);
-
- let key = file_name.to_string();
- let node = file_name.replace(".", " ").replace("_", " ");
- let cmd_type = format!("cmds::cmd::{}::JV{}Command", file_name, pascal_name);
-
- nodes.push(node.clone());
- commands.push((key, node, cmd_type));
- }
- }
-
- // Extract the node_if template from the template content
- const PROCESS_MARKER: &str = "// PROCESS";
- const TEMPLATE_START: &str = "// -- TEMPLATE START --";
- const TEMPLATE_END: &str = "// -- TEMPLATE END --";
- const LINE: &str = "<<LINE>>";
- const NODES: &str = "<<NODES>>";
-
- let template_start_index = template
- .find(TEMPLATE_START)
- .ok_or("Template start marker not found")?;
- let template_end_index = template
- .find(TEMPLATE_END)
- .ok_or("Template end marker not found")?;
-
- let template_slice = &template[template_start_index..template_end_index + TEMPLATE_END.len()];
- let node_if_template = template_slice
- .trim_start_matches(TEMPLATE_START)
- .trim_end_matches(TEMPLATE_END)
- .trim_matches('\n');
-
- // Generate the match arms for each command
- let match_arms: String = commands
- .iter()
- .map(|(key, node, cmd_type)| {
- node_if_template
- .replace("<<KEY>>", key)
- .replace("<<NODE_NAME>>", node)
- .replace("<<COMMAND_TYPE>>", cmd_type)
- .trim_matches('\n')
- .to_string()
- })
- .collect::<Vec<_>>()
- .join("\n");
-
- let nodes_str = format!(
- "[\n {}\n ]",
- nodes
- .iter()
- .map(|node| format!("\"{}\".to_string()", node))
- .collect::<Vec<_>>()
- .join(", ")
- );
-
- // Replace the template section with the generated match arms
- let final_content = template
- .replace(node_if_template, "")
- .replace(TEMPLATE_START, "")
- .replace(TEMPLATE_END, "")
- .replace(PROCESS_MARKER, &match_arms)
- .lines()
- .filter(|line| !line.trim().is_empty())
- .collect::<Vec<_>>()
- .join("\n")
- .replace(LINE, "")
- .replace(NODES, nodes_str.as_str());
-
- // Write the generated code
- std::fs::write(output_path, final_content)?;
-
- println!("Generated registry file with {} commands", commands.len());
- Ok(())
-}
-
-/// Generate renderer list file from Registry.toml configuration
-fn generate_renderer_list_file(repo_root: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
- let template_path = repo_root.join(RENDERER_LIST_TEMPLATE);
- let output_path = repo_root.join(RENDERER_LIST);
- let config_path = repo_root.join(REGISTRY_TOML);
-
- // Read the template
- let template = std::fs::read_to_string(&template_path)?;
-
- // Read and parse the TOML configuration
- let config_content = std::fs::read_to_string(&config_path)?;
- let config: toml::Value = toml::from_str(&config_content)?;
-
- // Collect all renderer configurations
- let mut renderers = Vec::new();
-
- if let Some(table) = config.as_table() {
- if let Some(renderer_table) = table.get("renderer") {
- if let Some(renderer_table) = renderer_table.as_table() {
- for (_, renderer_value) in renderer_table {
- if let Some(renderer_config) = renderer_value.as_table() {
- if let (Some(name), Some(renderer_type)) = (
- renderer_config.get("name").and_then(|v| v.as_str()),
- renderer_config.get("type").and_then(|v| v.as_str()),
- ) {
- renderers.push((name.to_string(), renderer_type.to_string()));
- }
- }
- }
- }
- }
- }
-
- // Extract the template section from the template content
- const MATCH_MARKER: &str = "// MATCH";
- const TEMPLATE_START: &str = "// -- TEMPLATE START --";
- const TEMPLATE_END: &str = "// -- TEMPLATE END --";
-
- let template_start_index = template
- .find(TEMPLATE_START)
- .ok_or("Template start marker not found")?;
- let template_end_index = template
- .find(TEMPLATE_END)
- .ok_or("Template end marker not found")?;
-
- let template_slice = &template[template_start_index..template_end_index + TEMPLATE_END.len()];
- let renderer_template = template_slice
- .trim_start_matches(TEMPLATE_START)
- .trim_end_matches(TEMPLATE_END)
- .trim_matches('\n');
-
- // Generate the match arms for each renderer
- let match_arms: String = renderers
- .iter()
- .map(|(name, renderer_type)| {
- renderer_template
- .replace("<<NAME>>", name)
- .replace("RendererType", renderer_type)
- .trim_matches('\n')
- .to_string()
})
- .collect::<Vec<String>>()
- .join("\n");
-
- // Replace the template section with the generated match arms
- let final_content = template
- .replace(renderer_template, "")
- .replace(TEMPLATE_START, "")
- .replace(TEMPLATE_END, "")
- .replace(MATCH_MARKER, &match_arms)
- .lines()
- .filter(|line| !line.trim().is_empty())
- .collect::<Vec<_>>()
- .join("\n");
-
- // Write the generated code
- std::fs::write(output_path, final_content)?;
-
- println!(
- "Generated renderer list file with {} renderers",
- renderers.len()
);
- Ok(())
-}
-
-/// Generate collect files from directory structure
-fn generate_collect_files(repo_root: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
- // Read and parse the TOML configuration
- let config_path = repo_root.join(REGISTRY_TOML);
- let config_content = std::fs::read_to_string(&config_path)?;
- let config: toml::Value = toml::from_str(&config_content)?;
-
- // Process each collect configuration
- let collect_table = config.get("collect").and_then(|v| v.as_table());
-
- let collect_table = match collect_table {
- Some(table) => table,
- None => return Ok(()),
- };
-
- for (_collect_name, collect_config) in collect_table {
- let config_table = match collect_config.as_table() {
- Some(table) => table,
- None => continue,
- };
-
- let path_str = match config_table.get("path").and_then(|v| v.as_str()) {
- Some(path) => path,
- None => continue,
- };
-
- let output_path = repo_root.join(path_str);
-
- // Extract directory name from the path (e.g., "src/renderers.rs" -> "renderers")
- let dir_name = match output_path.file_stem().and_then(|s| s.to_str()) {
- Some(name) => name.to_string(),
- None => continue,
- };
-
- // Get the directory path for this collect type
- // e.g., for "src/renderers.rs", we want "src/renderers/"
- let output_parent = output_path.parent().unwrap_or_else(|| repo_root.as_path());
- let dir_path = output_parent.join(&dir_name);
-
- // Collect all .rs files in the directory (excluding the output file itself)
- let mut modules = Vec::new();
-
- if dir_path.exists() && dir_path.is_dir() {
- for entry in std::fs::read_dir(&dir_path)? {
- let entry = entry?;
- let path = entry.path();
-
- if !path.is_file() {
- continue;
- }
-
- let extension = match path.extension() {
- Some(ext) => ext,
- None => continue,
- };
-
- if extension != "rs" {
- continue;
- }
-
- let file_name = match path.file_stem().and_then(|s| s.to_str()) {
- Some(name) => name,
- None => continue,
- };
-
- // Skip files that start with underscore
- if !file_name.starts_with('_') {
- modules.push(file_name.to_string());
- }
- }
- }
-
- // Sort modules alphabetically
- modules.sort();
-
- // Generate the content
- let mut content = String::new();
- for module in &modules {
- content.push_str(&format!("pub mod {};\n", module));
- }
-
- // Write the file
- std::fs::write(&output_path, content)?;
-
- println!(
- "Generated {} with {} modules: {:?}",
- path_str,
- modules.len(),
- modules
- );
- }
-
- Ok(())
}