Skip to main content

project_C/
main.rs

1//! Example: Building a C project with yamake.
2//!
3//! This example demonstrates how to use yamake to build a simple C project
4//! consisting of multiple source files, a static library, and an executable.
5//!
6//! # Project Structure
7//!
8//! ```text
9//! project_C/
10//! ├── main.c      # Main source file
11//! ├── add.c       # Library source file
12//! ├── add.h       # Library header
13//! └── wrapper.h   # Wrapper header (includes add.h)
14//! ```
15//!
16//! # Build Graph
17//!
18//! The build creates the following dependency graph:
19//!
20//! ```text
21//! main.c ──► main.o ──────────────────────► app
22//!                                            ▲
23//! add.c ───► add.o ───► libproject.a ────────┘
24//! ```
25//!
26//! # Usage
27//!
28//! ```bash
29//! cargo run --example project_C -- -s demo_projects -b /tmp/sandbox
30//! ```
31//!
32//! # Key Concepts
33//!
34//! - **Root nodes**: Source files (`CFile`, `HFile`) that don't need building
35//! - **Build nodes**: Output files (`OFile`, `AFile`, `XFile`) that are built
36//! - **Edges**: Dependencies between nodes (source → object → executable)
37//! - **Automatic scanning**: Header dependencies are discovered automatically
38
39// ANCHOR: use
40use argh::FromArgs;
41use log::info;
42use std::path::PathBuf;
43use yamake::c_nodes::{AFile, CFile, HFile, OFile, XFile};
44use yamake::model::G;
45// ANCHOR_END: use
46
47// ANCHOR: use_existing_rules
48// Using existing C build rules from yamake::c_nodes:
49// - CFile: C source files (.c)
50// - HFile: Header files (.h)
51// - OFile: Object files (.o)
52// - AFile: Static library files (.a)
53// - XFile: Executable files
54// ANCHOR_END: use_existing_rules
55
56/// Command-line arguments for the project_C example.
57#[derive(FromArgs)]
58/// A C project build example
59struct Args {
60    /// source directory
61    #[argh(option, short = 's')]
62    src: PathBuf,
63
64    /// sandbox directory
65    #[argh(option, short = 'b')]
66    sandbox: PathBuf,
67}
68
69fn main() {
70    env_logger::init();
71
72    let args: Args = argh::from_env();
73
74    let srcdir = args.src;
75    let sandbox = args.sandbox;
76
77    // ANCHOR: instanciate
78    let mut g = G::new(srcdir, sandbox);
79    // ANCHOR_END: instanciate
80
81    // ANCHOR: add_nodes
82    let main_c = g.add_root_node(CFile::new("project_C/main.c")).unwrap();
83    let main_o = g
84        .add_node(OFile::new("project_C/main.o", vec![], vec![]))
85        .unwrap();
86    let add_c = g.add_root_node(CFile::new("project_C/add.c")).unwrap();
87    let add_o = g
88        .add_node(OFile::new(
89            "project_C/add.o",
90            vec![],
91            vec!["-DYYY_defined".to_string()],
92        ))
93        .unwrap();
94    let _add_h = g.add_root_node(HFile::new("project_C/add.h")).unwrap();
95    let _wrapper_h = g.add_root_node(HFile::new("project_C/wrapper.h")).unwrap();
96    let project_a = g.add_node(AFile::new("project_C/libproject.a")).unwrap();
97    let app = g.add_node(XFile::new("project_C/app")).unwrap();
98    // ANCHOR_END: add_nodes
99
100    // ANCHOR: add_edges
101    g.add_edge(main_c, main_o);
102    g.add_edge(main_o, app);
103    g.add_edge(add_c, add_o);
104    g.add_edge(add_o, project_a);
105    g.add_edge(project_a, app);
106    // ANCHOR_END: add_edges
107
108    info!(
109        "Created graph with {} nodes and {} edges",
110        g.g.node_count(),
111        g.g.edge_count()
112    );
113
114    // ANCHOR: make
115    // make() will:
116    // 1. Mount root nodes (copy source files to sandbox)
117    // 2. Scan for dependencies (adds edges for #include directives)
118    // 3. Build all nodes in dependency order (parallel where possible)
119    let success = g.make();
120    info!("Build {}", if success { "succeeded" } else { "failed" });
121    // ANCHOR_END: make
122}