summaryrefslogtreecommitdiff
path: root/subplot-build/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'subplot-build/src/lib.rs')
-rw-r--r--subplot-build/src/lib.rs83
1 files changed, 83 insertions, 0 deletions
diff --git a/subplot-build/src/lib.rs b/subplot-build/src/lib.rs
new file mode 100644
index 0000000..89ea57d
--- /dev/null
+++ b/subplot-build/src/lib.rs
@@ -0,0 +1,83 @@
+//! Subplot test program building from `build.rs`
+//!
+//! This crate provides the Subplot code generation facility in a way
+//! that's meant to be easy to use from another project's `build.rs`
+//! module.
+
+use std::env::var_os;
+use std::fmt::Debug;
+use std::path::{Path, PathBuf};
+use subplot::{get_basedir_from, Result};
+use tracing::{event, instrument, span, Level};
+
+/// Generate code for one document, inside `build.rs`.
+///
+/// The output files will be written to the directory specified in the
+/// OUT_DIR environment variable. That variable is set by Cargo, when
+/// a crate is built.
+///
+/// Also emit instructions for Cargo so it knows to re-run `build.rs`
+/// whenever the input subplot or any of the bindings or functions
+/// files it refers to changes. See
+/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html> for
+/// details.
+///
+/// ```
+/// use subplot_build::codegen;
+/// # let dir = tempfile::tempdir().unwrap();
+/// # std::env::set_var("OUT_DIR", dir.path());
+///
+/// codegen("foo.md").ok(); // ignoring error to keep example short
+/// # dir.close().unwrap()
+/// ```
+#[instrument(level = "trace")]
+pub fn codegen<P>(filename: P) -> Result<()>
+where
+ P: AsRef<Path> + Debug,
+{
+ let span = span!(Level::TRACE, "codegen_buildrs");
+ let _enter = span.enter();
+
+ event!(Level::TRACE, "Generating code in build.rs");
+
+ // Decide the name of the generated test program.
+ let out_dir = var_os("OUT_DIR").expect("OUT_DIR is not defined in the environment");
+ let out_dir = Path::new(&out_dir);
+ let filename = filename.as_ref();
+ let test_rs =
+ buildrs_output(out_dir, filename, "rs").expect("could not create output filename");
+
+ // Generate test program.
+ let output = subplot::codegen(filename, &test_rs)?;
+
+ // Write instructions for Cargo to check if build scripts needs
+ // re-running.
+ let base_path = get_basedir_from(filename);
+ let meta = output.doc.meta();
+ buildrs_deps(&base_path, &meta.bindings_filenames());
+ buildrs_deps(&base_path, &meta.functions_filenames());
+ buildrs_deps(&base_path, &[filename]);
+
+ event!(Level::TRACE, "Finished generating code");
+ Ok(())
+}
+
+fn buildrs_deps(base_path: &Path, filenames: &[&Path]) {
+ for filename in filenames {
+ let filename = base_path.join(filename);
+ if filename.exists() {
+ println!("cargo:rerun-if-changed={}", filename.display());
+ }
+ }
+}
+
+fn buildrs_output(dir: &Path, filename: &Path, new_extension: &str) -> Option<PathBuf> {
+ if let Some(basename) = filename.file_name() {
+ let basename = Path::new(basename);
+ if let Some(stem) = basename.file_stem() {
+ let stem = Path::new(stem);
+ return Some(dir.join(stem.with_extension(new_extension)));
+ }
+ }
+ None
+}