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}