1use std::env::var_os;
8use std::error::Error;
9use std::fmt::Debug;
10use std::path::{Path, PathBuf};
11use subplot::get_basedir_from;
12pub use subplot::SubplotError;
13use tracing::{event, instrument, span, Level};
14
15#[instrument(level = "trace")]
36pub fn codegen<P>(filename: P) -> Result<(), SubplotError>
37where
38 P: AsRef<Path> + Debug,
39{
40 let filename = filename.as_ref();
41 match _codegen(filename) {
42 Ok(()) => Ok(()),
43 Err(e) => {
44 eprintln!(
45 "\n\n\nsubplot_build::codegen({}) failed: {e}",
46 filename.display(),
47 );
48 let mut es = e.source();
49 while let Some(source) = es {
50 eprintln!("caused by: {source}");
51 es = source.source();
52 }
53 eprintln!("\n\n");
54 Err(e)
55 }
56 }
57}
58
59fn _codegen(filename: &Path) -> Result<(), SubplotError> {
60 let span = span!(Level::TRACE, "codegen_buildrs");
61 let _enter = span.enter();
62
63 event!(Level::TRACE, "Generating code in build.rs");
64
65 let out_dir = var_os("OUT_DIR").expect("OUT_DIR is not defined in the environment");
67 let out_dir = Path::new(&out_dir);
68 let test_rs =
69 buildrs_output(out_dir, filename, "rs").expect("could not create output filename");
70
71 let output = subplot::codegen(filename, &test_rs, Some("rust"))?;
73
74 let base_path = get_basedir_from(filename);
77 let meta = output.doc.meta();
78 for filename in meta.markdown_filenames() {
79 buildrs_deps(&base_path, Some(filename.as_path()));
80 }
81 buildrs_deps(&base_path, meta.bindings_filenames());
82 let docimpl = output
83 .doc
84 .meta()
85 .document_impl("rust")
86 .expect("We managed to codegen rust, yet the spec is missing?");
87 buildrs_deps(&base_path, docimpl.functions_filenames());
88 buildrs_deps(&base_path, Some(filename));
89
90 event!(Level::TRACE, "Finished generating code");
91 Ok(())
92}
93
94fn buildrs_deps<'a>(base_path: &Path, filenames: impl IntoIterator<Item = &'a Path>) {
95 for filename in filenames {
96 let filename = base_path.join(filename);
97 if filename.exists() {
98 println!("cargo:rerun-if-changed={}", filename.display());
99 }
100 }
101}
102
103fn buildrs_output(dir: &Path, filename: &Path, new_extension: &str) -> Option<PathBuf> {
104 if let Some(basename) = filename.file_name() {
105 let basename = Path::new(basename);
106 if let Some(stem) = basename.file_stem() {
107 let stem = Path::new(stem);
108 return Some(dir.join(stem.with_extension(new_extension)));
109 }
110 }
111 None
112}