// Copyright (C) 2026 Elias Wendland // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3 exclusively. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use std::env; use std::fs; use std::path::Path; use std::process::Command; use clap::CommandFactory; #[path = "src/args.rs"] mod args; fn run_command(cmd: &str, args: &[&str]) -> Option { let output = Command::new(cmd).args(args).output().ok()?; if output.status.success() { let s = String::from_utf8_lossy(&output.stdout).trim().to_string(); if !s.is_empty() { return Some(s); } } None } fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("plugins_gen.rs"); // Read the plugins directory let plugins_dir = Path::new("plugins"); let mut plugins_code = String::new(); let mut registry_add_code = String::new(); if plugins_dir.exists() { for entry in fs::read_dir(plugins_dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); if path.extension().and_then(|s| s.to_str()) == Some("rs") { let stem = path.file_stem().unwrap().to_str().unwrap(); // We'll use include! macro to include the file content directly. // However, `include!` requires a valid path. The generated file is in OUT_DIR, // so we need an absolute path or relative to the generated file. let abs_path = path.canonicalize().unwrap(); let abs_path_str = abs_path.to_str().unwrap(); plugins_code.push_str(&format!( "pub mod {} {{\n include!({:?});\n}}\n", stem, abs_path_str )); registry_add_code .push_str(&format!("registry.push(Box::new({}::PluginImpl));\n", stem)); } } } let full_code = format!( " {} pub fn get_plugins() -> Vec> {{ let mut registry: Vec> = Vec::new(); {} registry }} ", plugins_code, registry_add_code ); fs::write(&dest_path, full_code).unwrap(); println!("cargo:rerun-if-changed=plugins"); // Add build metadata for versioning let version = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "dev-unknown".to_string()); let build_time = run_command("date", &["-u", "+%Y-%m-%d %H:%M:%S UTC"]) .unwrap_or_else(|| "Unknown".to_string()); let git_commit = run_command("git", &["rev-parse", "--short", "HEAD"]) .unwrap_or_else(|| "Unknown".to_string()); let target = env::var("TARGET").unwrap_or_else(|_| "Unknown".to_string()); let profile = env::var("PROFILE").unwrap_or_else(|_| "Unknown".to_string()); let rustc_path = env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); let rustc_version = run_command(&rustc_path, &["--version"]) .map(|s| s.split('\n').next().unwrap_or("").to_string()) .unwrap_or_else(|| "Unknown".to_string()); let host = env::var("HOST").unwrap_or_else(|_| "Unknown".to_string()); let authors = env::var("CARGO_PKG_AUTHORS").unwrap_or_else(|_| "Unknown".to_string()); let repository = env::var("CARGO_PKG_REPOSITORY").unwrap_or_else(|_| "Unknown".to_string()); let version_string = format!( "{}\nbuild-time: {}\ncommit: {}\ntarget: {}\nhost: {}\nprofile: {}\nrustc: {}\nauthors: {}\nrepository: {}", version, build_time, git_commit, target, host, profile, rustc_version, authors, repository ); let version_path = Path::new(&out_dir).join("version.txt"); fs::write(&version_path, &version_string).unwrap(); // Generate man page let man_dir = Path::new("target").join("man"); fs::create_dir_all(&man_dir).unwrap(); let mut cmd = args::Args::command(); cmd = cmd.name("convertis").version(env!("CARGO_PKG_VERSION")); let man = clap_mangen::Man::new(cmd); let mut buffer: Vec = Default::default(); man.render(&mut buffer).unwrap(); fs::write(man_dir.join("convertis.1"), buffer).unwrap(); }