From fe8a8f2b94f438971f15279edc375f99dd34815f Mon Sep 17 00:00:00 2001 From: stringhandler Date: Wed, 15 Apr 2026 15:03:22 +0200 Subject: [PATCH 1/2] feat: add build.rs compatiblity --- Cargo.lock | 11 +- Cargo.toml | 3 +- crates/build/Cargo.toml | 2 +- crates/cli/Cargo.toml | 2 +- crates/cli/src/commands/build.rs | 2 +- crates/cli/src/commands/clean.rs | 2 +- crates/cli/src/commands/error.rs | 2 +- crates/cli/src/config/core.rs | 2 +- crates/macros/Cargo.toml | 2 +- crates/macros/src/lib.rs | 4 +- crates/smplx-build/Cargo.toml | 14 +++ crates/smplx-build/src/lib.rs | 179 +++++++++++++++++++++++++++++++ examples/basic/Cargo.lock | 22 ++-- examples/basic/Cargo.toml | 3 + examples/basic/build.rs | 3 + 15 files changed, 234 insertions(+), 19 deletions(-) create mode 100644 crates/smplx-build/Cargo.toml create mode 100644 crates/smplx-build/src/lib.rs create mode 100644 examples/basic/build.rs diff --git a/Cargo.lock b/Cargo.lock index 6fb0ee1..51444bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,6 +1290,13 @@ dependencies = [ [[package]] name = "smplx-build" +version = "0.0.1" +dependencies = [ + "smplx-build-internal", +] + +[[package]] +name = "smplx-build-internal" version = "0.0.3" dependencies = [ "glob", @@ -1316,7 +1323,7 @@ dependencies = [ "minreq", "serde", "serde_json", - "smplx-build", + "smplx-build-internal", "smplx-regtest", "smplx-sdk", "smplx-test", @@ -1330,7 +1337,7 @@ dependencies = [ name = "smplx-macros" version = "0.0.3" dependencies = [ - "smplx-build", + "smplx-build-internal", "smplx-test", "syn", ] diff --git a/Cargo.toml b/Cargo.toml index fc68e9b..3f8c161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,8 @@ multiple_crate_versions = "allow" [workspace.dependencies] smplx-macros = { path = "./crates/macros", version = "0.0.3" } -smplx-build = { path = "./crates/build", version = "0.0.3" } +smplx-build-internal = { path = "./crates/build", version = "0.0.3" } +smplx-build = { path = "./crates/smplx-build", version = "0.0.1" } smplx-test = { path = "./crates/test", version = "0.0.3" } smplx-regtest = { path = "./crates/regtest", version = "0.0.3" } smplx-sdk = { path = "./crates/sdk", version = "0.0.3" } diff --git a/crates/build/Cargo.toml b/crates/build/Cargo.toml index fdc82dd..3de3fe3 100644 --- a/crates/build/Cargo.toml +++ b/crates/build/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "smplx-build" +name = "smplx-build-internal" version = "0.0.3" description = "Simplex build command internal implementation" license.workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 8212f7d..cedec00 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -17,7 +17,7 @@ workspace = true [dependencies] smplx-regtest = { workspace = true } smplx-test = { workspace = true } -smplx-build = { workspace = true } +smplx-build-internal = { workspace = true } smplx-sdk = { workspace = true } thiserror = { workspace = true } diff --git a/crates/cli/src/commands/build.rs b/crates/cli/src/commands/build.rs index 337e7f2..6a81aca 100644 --- a/crates/cli/src/commands/build.rs +++ b/crates/cli/src/commands/build.rs @@ -1,4 +1,4 @@ -use smplx_build::{ArtifactsGenerator, ArtifactsResolver, BuildConfig}; +use smplx_build_internal::{ArtifactsGenerator, ArtifactsResolver, BuildConfig}; use super::error::CommandError; diff --git a/crates/cli/src/commands/clean.rs b/crates/cli/src/commands/clean.rs index 586def0..d7579ff 100644 --- a/crates/cli/src/commands/clean.rs +++ b/crates/cli/src/commands/clean.rs @@ -1,6 +1,6 @@ use std::{fmt::Display, fs, path::PathBuf}; -use smplx_build::{ArtifactsResolver, BuildConfig}; +use smplx_build_internal::{ArtifactsResolver, BuildConfig}; use crate::commands::error::CleanError; use crate::commands::error::CommandError; diff --git a/crates/cli/src/commands/error.rs b/crates/cli/src/commands/error.rs index 38e27c1..fc323b8 100644 --- a/crates/cli/src/commands/error.rs +++ b/crates/cli/src/commands/error.rs @@ -12,7 +12,7 @@ pub enum CommandError { Test(#[from] smplx_test::error::TestError), #[error(transparent)] - Build(#[from] smplx_build::error::BuildError), + Build(#[from] smplx_build_internal::error::BuildError), #[error(transparent)] Init(#[from] InitError), diff --git a/crates/cli/src/config/core.rs b/crates/cli/src/config/core.rs index 14caf75..0c35363 100644 --- a/crates/cli/src/config/core.rs +++ b/crates/cli/src/config/core.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use std::path::{Path, PathBuf}; -use smplx_build::BuildConfig; +use smplx_build_internal::BuildConfig; use smplx_regtest::RegtestConfig; use smplx_test::TestConfig; diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index 9a71fbf..00bc7bd 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -14,7 +14,7 @@ proc-macro = true workspace = true [dependencies] -smplx-build = { workspace = true } +smplx-build-internal = { workspace = true } smplx-test = { workspace = true } syn = { version = "2.0.114", default-features = false, features = ["parsing", "proc-macro"] } diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index c80bed3..3a4dd58 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -2,9 +2,9 @@ use proc_macro::TokenStream; #[proc_macro] pub fn include_simf(tokenstream: TokenStream) -> TokenStream { - let input = syn::parse_macro_input!(tokenstream as smplx_build::macros::parse::SynFilePath); + let input = syn::parse_macro_input!(tokenstream as smplx_build_internal::macros::parse::SynFilePath); - match smplx_build::macros::expand(&input) { + match smplx_build_internal::macros::expand(&input) { Ok(ts) => ts.into(), Err(e) => e.to_compile_error().into(), } diff --git a/crates/smplx-build/Cargo.toml b/crates/smplx-build/Cargo.toml new file mode 100644 index 0000000..2a37741 --- /dev/null +++ b/crates/smplx-build/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "smplx-build" +version = "0.0.1" +description = "Build script helper for generating Simplex contract artifacts" +license.workspace = true +edition.workspace = true +repository = "https://github.com/BlockstreamResearch/smplx" +readme = "../../README.md" + +[lints] +workspace = true + +[dependencies] +smplx-build-internal = { workspace = true } diff --git a/crates/smplx-build/src/lib.rs b/crates/smplx-build/src/lib.rs new file mode 100644 index 0000000..135bbfa --- /dev/null +++ b/crates/smplx-build/src/lib.rs @@ -0,0 +1,179 @@ +use std::path::{Path, PathBuf}; + +pub use smplx_build_internal::error::BuildError; +use smplx_build_internal::{ArtifactsGenerator, ArtifactsResolver, BuildConfig}; + +#[allow(clippy::needless_doctest_main)] +/// Builder for configuring and running Simplex artifact generation from `build.rs`. +/// +/// Settings are resolved in priority order (highest wins): +/// 1. Values set directly on the builder (`.src_dir(...)`, etc.) +/// 2. Values loaded from a `Simplex.toml` config file (`.config(...)`) +/// 3. Built-in defaults (`simf/`, `**/*.simf`, `src/artifacts`) +/// +/// # Examples +/// +/// No config file — configure everything in `build.rs`: +/// ```no_run +/// fn main() { +/// smplx_build::Builder::new() +/// .src_dir("simf") +/// .out_dir("src/artifacts") +/// .simf_files(["**/*.simf"]) +/// .generate() +/// .unwrap(); +/// } +/// ``` +/// +/// Load from `Simplex.toml` but override the output directory: +/// ```no_run +/// fn main() { +/// smplx_build::Builder::new() +/// .config("Simplex.toml") +/// .out_dir("src/generated") +/// .generate() +/// .unwrap(); +/// } +/// ``` +pub struct Builder { + config_path: Option, + src_dir: Option, + out_dir: Option, + simf_files: Option>, +} + +impl Builder { + pub fn new() -> Self { + Self { + config_path: None, + src_dir: None, + out_dir: None, + simf_files: None, + } + } + + /// Load base settings from a `Simplex.toml` file. + /// Fields set directly on the builder take precedence over values in the file. + pub fn config(mut self, path: impl AsRef) -> Self { + self.config_path = Some(path.as_ref().to_path_buf()); + self + } + + /// Directory containing `.simf` source files (default: `simf`). + pub fn src_dir(mut self, dir: impl Into) -> Self { + self.src_dir = Some(dir.into()); + self + } + + /// Directory where generated Rust artifacts are written (default: `src/artifacts`). + pub fn out_dir(mut self, dir: impl Into) -> Self { + self.out_dir = Some(dir.into()); + self + } + + /// Glob patterns selecting which `.simf` files to compile (default: `["**/*.simf"]`). + /// Replaces the full list — call once with all patterns you need. + pub fn simf_files(mut self, patterns: impl IntoIterator>) -> Self { + self.simf_files = Some(patterns.into_iter().map(Into::into).collect()); + self + } + + /// Run artifact generation. + /// + /// Emits `cargo:rerun-if-changed` directives for the config file (if any) + /// and every resolved `.simf` input file. + pub fn generate(self) -> Result<(), BuildError> { + // Start from defaults, then overlay the config file (if provided). + let mut config = BuildConfig::default(); + + if let Some(ref path) = self.config_path { + if !path.exists() { + return Err(BuildError::Io(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("config file not found: {}", path.display()), + ))); + } + config = BuildConfig::from_file(path)?; + println!("cargo:rerun-if-changed={}", path.display()); + } + + // Builder fields take highest precedence. + if let Some(src_dir) = self.src_dir { + config.src_dir = src_dir; + } + if let Some(out_dir) = self.out_dir { + config.out_dir = out_dir; + } + if let Some(simf_files) = self.simf_files { + config.simf_files = simf_files; + } + + let out_dir = ArtifactsResolver::resolve_local_dir(&config.out_dir)?; + let src_dir = { + let p = ArtifactsResolver::resolve_local_dir(&config.src_dir)?; + // resolve_files_to_build canonicalizes paths (producing \\?\ UNC paths on + // Windows), so canonicalize src_dir too so strip_prefix works correctly. + if p.exists() { p.canonicalize()? } else { p } + }; + let files = ArtifactsResolver::resolve_files_to_build(&config.src_dir, &config.simf_files)?; + + for file in &files { + println!("cargo:rerun-if-changed={}", file.display()); + } + + ArtifactsGenerator::generate_artifacts(&out_dir, &src_dir, &files)?; + + Ok(()) + } +} + +impl Default for Builder { + fn default() -> Self { + Self::new() + } +} + +#[allow(clippy::needless_doctest_main)] +/// Generate Simplex contract artifacts from a `build.rs` script. +/// +/// Reads the `[build]` section of `config_path` (a `Simplex.toml` file) and +/// generates Rust bindings for every `.simf` contract found. Equivalent to +/// `Builder::new().config(config_path).generate()`. +/// +/// # Example +/// +/// ```no_run +/// fn main() { +/// smplx_build::generate_artifacts("Simplex.toml").unwrap(); +/// } +/// ``` +pub fn generate_artifacts(config_path: impl AsRef) -> Result<(), BuildError> { + Builder::new().config(config_path).generate() +} + +/// Convenience macro for use in `build.rs`. +/// +/// Calls [`generate_artifacts`] and panics with a clear message on failure. +/// +/// ```no_run +/// // uses "Simplex.toml" by default +/// fn main() { +/// smplx_build::generate_artifacts!(); +/// } +/// +/// // explicit config path +/// fn main() { +/// smplx_build::generate_artifacts!("Simplex.toml"); +/// } +/// ``` +#[macro_export] +macro_rules! generate_artifacts { + ($config:expr) => { + if let Err(e) = $crate::generate_artifacts($config) { + panic!("smplx-build: failed to generate artifacts: {}", e); + } + }; + () => { + $crate::generate_artifacts!("Simplex.toml") + }; +} diff --git a/examples/basic/Cargo.lock b/examples/basic/Cargo.lock index 30cc944..dceb087 100644 --- a/examples/basic/Cargo.lock +++ b/examples/basic/Cargo.lock @@ -1158,6 +1158,7 @@ name = "simplex_example" version = "0.1.0" dependencies = [ "anyhow", + "smplx-build", "smplx-std", ] @@ -1209,7 +1210,14 @@ dependencies = [ [[package]] name = "smplx-build" -version = "0.0.2" +version = "0.0.1" +dependencies = [ + "smplx-build-internal", +] + +[[package]] +name = "smplx-build-internal" +version = "0.0.3" dependencies = [ "glob", "globwalk", @@ -1226,16 +1234,16 @@ dependencies = [ [[package]] name = "smplx-macros" -version = "0.0.2" +version = "0.0.3" dependencies = [ - "smplx-build", + "smplx-build-internal", "smplx-test", "syn", ] [[package]] name = "smplx-regtest" -version = "0.0.2" +version = "0.0.3" dependencies = [ "electrsd", "serde", @@ -1246,7 +1254,7 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.2" +version = "0.0.3" dependencies = [ "bip39", "bitcoin_hashes", @@ -1264,7 +1272,7 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.2" +version = "0.0.3" dependencies = [ "either", "serde", @@ -1276,7 +1284,7 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.2" +version = "0.0.3" dependencies = [ "electrsd", "proc-macro2", diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml index 2373e16..c2e200d 100644 --- a/examples/basic/Cargo.toml +++ b/examples/basic/Cargo.toml @@ -4,6 +4,9 @@ edition = "2024" rust-version = "1.91.0" version = "0.1.0" +[build-dependencies] +smplx-build = { path = "../../crates/smplx-build" } + [dependencies] smplx-std = { path = "../../crates/simplex" } diff --git a/examples/basic/build.rs b/examples/basic/build.rs new file mode 100644 index 0000000..02a0907 --- /dev/null +++ b/examples/basic/build.rs @@ -0,0 +1,3 @@ +fn main() { + smplx_build::generate_artifacts!(); +} From 208f94091b435d12724cd666567f1c2fdde72574 Mon Sep 17 00:00:00 2001 From: stringhandler Date: Wed, 15 Apr 2026 16:47:46 +0200 Subject: [PATCH 2/2] fix doc tests --- crates/smplx-build/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/smplx-build/src/lib.rs b/crates/smplx-build/src/lib.rs index 135bbfa..a26a482 100644 --- a/crates/smplx-build/src/lib.rs +++ b/crates/smplx-build/src/lib.rs @@ -160,7 +160,10 @@ pub fn generate_artifacts(config_path: impl AsRef) -> Result<(), BuildErro /// fn main() { /// smplx_build::generate_artifacts!(); /// } +/// ``` /// +/// With explicit config +/// ```no_run /// // explicit config path /// fn main() { /// smplx_build::generate_artifacts!("Simplex.toml");