weld_codegen/
rust_build.rs

1//! The build logic inside build.rs - rebuild model,
2//! using instructions in codegen.toml. Emits "rererun-if-changed"
3//! instructions to cargo so model is only rebuilt if either codegen.toml
4//! or one of the model files changes.
5
6use std::path::PathBuf;
7
8use crate::error::Error;
9
10/// wraps the logic inside build.rs
11pub fn rust_build<P: Into<PathBuf>>(
12    config_path: P,
13) -> std::result::Result<(), Box<dyn std::error::Error>> {
14    rust_build_into(config_path, ".")
15}
16
17/// Generate rust code using the codegen.toml config file and output directory.
18/// `config_path` should be relative to the directory containing build.rs (or absolute).
19/// `output_relative_dir`, the location of generated files, should either be an absolute path,
20/// or a path relative to the folder containing the codegen.toml file.
21/// To use rustc's build folder, from inside build.rs, use `&std::env::var("OUT_DIR").unwrap()`
22pub fn rust_build_into<CFG: Into<PathBuf>, OUT: Into<PathBuf>>(
23    config_path: CFG,
24    output_relative_dir: OUT,
25) -> std::result::Result<(), Box<dyn std::error::Error>> {
26    use crate::{
27        config::{CodegenConfig, OutputLanguage},
28        Generator,
29    };
30    let config_path = config_path.into();
31    if !config_path.is_file() {
32        return Err(Error::Build(format!("missing config file {}", &config_path.display())).into());
33    }
34    let config_path = std::fs::canonicalize(config_path)?;
35    let config_file = std::fs::read_to_string(&config_path).map_err(|e| {
36        Error::Build(format!(
37            "error reading config file '{}': {}",
38            &config_path.display(),
39            e
40        ))
41    })?;
42    let mut config = config_file
43        .parse::<CodegenConfig>()
44        .map_err(|e| Error::Build(format!("parsing config: {e}")))?;
45    config.base_dir = config_path.parent().unwrap().to_path_buf();
46    config.output_languages = vec![OutputLanguage::Rust];
47
48    // tell cargo to rebuild if codegen.toml changes
49    println!("cargo:rerun-if-changed={}", &config_path.display());
50
51    let model = crate::sources_to_model(&config.models, &config.base_dir, 0)?;
52
53    if let Err(msgs) = crate::validate::validate(&model) {
54        return Err(Box::new(Error::Build(msgs.join("\n"))));
55    }
56
57    // the second time we do this it should be faster since no downloading is required,
58    // and we also don't invoke assembler to traverse directories
59    for path in crate::sources_to_paths(&config.models, &config.base_dir, 0)?.into_iter() {
60        // rerun-if-changed works on directories and files, so it's ok that sources_to_paths
61        // may include folders that haven't been traversed by the assembler.
62        // Using a folder depends on the OS updating folder mtime if the folder contents change.
63        // In many cases, the model file for the primary interface/namespace will
64        // be a file path (it is in projects created with `weld create`).
65        // All paths returned from sources_to_paths are absolute (by joining to config.base_dir)
66        // so we don't need to adjust them here
67        if path.exists() {
68            println!("cargo:rerun-if-changed={}", &path.display());
69        }
70    }
71
72    Generator::default().gen(
73        Some(&model),
74        config,
75        Vec::new(),
76        &output_relative_dir.into(),
77        Vec::new(),
78    )?;
79
80    Ok(())
81}