1
//!
2
//! Package command for creating release distributions.
3
//!
4

            
5
use duct::cmd;
6
use std::env;
7
use std::error::Error;
8
use std::fs::copy;
9
use std::fs::create_dir_all;
10

            
11
/// Returns the platform-specific executable file name for a binary (adds `.exe` on Windows).
12
fn exe_name(binary_name: &str) -> String {
13
    if cfg!(windows) {
14
        format!("{binary_name}.exe")
15
    } else {
16
        binary_name.to_string()
17
    }
18
}
19

            
20
/// Builds the project in release mode and packages specified binaries into a
21
/// newly created 'package' directory.
22
pub(crate) fn package() -> Result<(), Box<dyn Error>> {
23
    // Get the workspace root directory
24
    let workspace_root = env::current_dir()?;
25

            
26
    // Precondition: Ensure we're in a valid Rust workspace
27
    debug_assert!(
28
        workspace_root.join("Cargo.toml").exists(),
29
        "Must be run from workspace root containing Cargo.toml"
30
    );
31

            
32
    println!("=== Creating package directory ===");
33

            
34
    // Create package directory for distribution artifacts
35
    let package_dir = workspace_root.join("package");
36
    create_dir_all(&package_dir)?;
37

            
38
    println!("=== Building and copying release binaries ===");
39

            
40
    // Mapping from workspace paths to their binaries
41
    let workspace_binaries = [
42
        (
43
            workspace_root.clone(),
44
            vec!["merc-lts", "merc-rewrite", "merc-vpg", "merc-sym"],
45
        ),
46
        (workspace_root.join("tools/gui"), vec!["merc-ltsgraph"]),
47
        (workspace_root.join("tools/mcrl2"), vec!["merc-pbes", "merc-lps"]),
48
    ];
49

            
50
    // All workspaces share the root `target/` directory: the `tools/gui` and `tools/mcrl2`
51
    // workspaces set `target-dir = "../../target"` in their `.cargo/config.toml`, so every
52
    // release binary ends up under `<workspace_root>/target/release` regardless of which
53
    // workspace built it.
54
    let target_release_dir = workspace_root.join("target").join("release");
55

            
56
    // Build all workspaces in release mode
57
    for (workspace_path, binaries) in &workspace_binaries {
58
        cmd!("cargo", "build", "--release").dir(workspace_path).run()?;
59

            
60
        for binary_name in binaries {
61
            let source_path = target_release_dir.join(exe_name(binary_name));
62
            let dest_path = package_dir.join(exe_name(binary_name));
63

            
64
            // Precondition: Binary must exist after successful build
65
            assert!(
66
                source_path.exists(),
67
                "Binary {binary_name} should exist after cargo build --release"
68
            );
69

            
70
            copy(&source_path, &dest_path)?;
71
            println!("Copied {binary_name} to package directory");
72
        }
73
    }
74

            
75
    println!("=== Package creation completed ===");
76
    println!("Package directory: {}", package_dir.display());
77

            
78
    // Add the LICENSE to the package
79
    let license_src = workspace_root.join("LICENSE");
80
    let license_dest = package_dir.join("LICENSE");
81
    copy(&license_src, &license_dest)?;
82

            
83
    // Add KaHyPar configuration used by the symbolic crate
84
    let kahypar_ini_src = workspace_root.join("crates/symbolic/data/kahypar.ini");
85
    let kahypar_ini_dest = package_dir.join("kahypar.ini");
86
    copy(&kahypar_ini_src, &kahypar_ini_dest)?;
87

            
88
    Ok(())
89
}