Skip to main content

project_expand/
main.rs

1//! Example: Dynamic code generation with expand.
2//!
3//! This example demonstrates yamake's `expand` feature, which allows nodes
4//! to dynamically generate additional nodes and edges during the build process.
5//!
6//! # How Expand Works
7//!
8//! 1. A YAML configuration file (`languages.yml`) defines available languages
9//! 2. `YmlDesc` (root node) is mounted to the sandbox
10//! 3. `JsonDesc` converts YAML to JSON and calls `expand()` after building
11//! 4. `expand()` generates C source files, headers, and object files for each language
12//! 5. These generated files are compiled into a static library (`liblangs.a`)
13//! 6. The main executable links against this library
14//!
15//! # Project Structure
16//!
17//! ```text
18//! project_expand/
19//! ├── main.c           # Main source (uses generated headers)
20//! ├── wrapper.h        # Includes generated languages.h
21//! ├── languages.yml    # Configuration: list of languages
22//! └── generated/       # Created by expand()
23//!     ├── languages.h  # Generated: function pointer array
24//!     ├── english.c    # Generated: "Hello, World!"
25//!     ├── english.h
26//!     ├── french.c     # Generated: "Bonjour!"
27//!     ├── french.h
28//!     └── liblangs.a   # Static library
29//! ```
30//!
31//! # Build Graph (after expand)
32//!
33//! ```text
34//! languages.yml ──► languages.json ──► [expand generates:]
35//!                                       ├── english.c ──► english.o ──┐
36//!                                       ├── french.c ──► french.o ────┼──► liblangs.a
37//!                                       └── languages.h               │         │
38//!                                                                     │         │
39//! main.c ──────────────────────────────► main.o ──────────────────────┴─────────┴──► app
40//! ```
41//!
42//! # Usage
43//!
44//! ```bash
45//! cargo run --example project_expand -- -s demo_projects -b /tmp/sandbox
46//! ```
47
48#[path = "../../tests/common/mod.rs"]
49mod common;
50
51use argh::FromArgs;
52use common::{JsonDesc, YmlDesc};
53use log::info;
54use std::fs;
55use std::path::PathBuf;
56use yamake::c_nodes::{AFile, CFile, HFile, OFile, XFile};
57use yamake::model::G;
58
59/// Command-line arguments for the project_expand example.
60#[derive(FromArgs)]
61/// A C project build example demonstrating expand functionality
62struct Args {
63    /// source directory
64    #[argh(option, short = 's')]
65    src: PathBuf,
66
67    /// sandbox directory
68    #[argh(option, short = 'b')]
69    sandbox: PathBuf,
70}
71
72fn main() {
73    env_logger::init();
74
75    let args: Args = argh::from_env();
76
77    let srcdir = args.src;
78    let sandbox = args.sandbox;
79
80    let mut g = G::new(srcdir.clone(), sandbox);
81
82    let main_c = g
83        .add_root_node(CFile::new("project_expand/main.c"))
84        .unwrap();
85    let main_o = g
86        .add_node(OFile::new("project_expand/main.o", vec![], vec![]))
87        .unwrap();
88    let _wrapper_h = g
89        .add_root_node(HFile::new("project_expand/wrapper.h"))
90        .unwrap();
91    let app = g.add_node(XFile::new("project_expand/app")).unwrap();
92
93    let languages_yml = g
94        .add_root_node(YmlDesc::new("project_expand/languages.yml"))
95        .unwrap();
96    let languages_json = g
97        .add_node(JsonDesc::new("project_expand/languages.json"))
98        .unwrap();
99    let liblangs = g
100        .add_node(AFile::new("project_expand/generated/liblangs.a"))
101        .unwrap();
102
103    g.add_edge(main_c, main_o);
104    g.add_edge(main_o, app);
105    g.add_edge(languages_yml, languages_json);
106    g.add_edge(languages_json, liblangs);
107    g.add_edge(liblangs, app);
108
109    info!(
110        "Created graph with {} nodes and {} edges",
111        g.g.node_count(),
112        g.g.edge_count()
113    );
114
115    let success = g.make();
116    info!("Build {}", if success { "succeeded" } else { "failed" });
117
118    // Write mermaid graph to file
119    let mermaid_path = srcdir.join("project_expand/graph.mermaid");
120    let mermaid = g.to_mermaid();
121    fs::write(&mermaid_path, &mermaid).expect("Failed to write graph.mermaid");
122    info!("Wrote mermaid graph to {}", mermaid_path.display());
123}