Repository: tangle-network/gadget (language: rust)

Issue:
[TASK] Build utilities for reduced code duplication in build scripts
# Overview

We should create a `blueprint-build-utils` library to reduce the amount of code in each blueprint's build script.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 330f97ecd..e7f7df403 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1880,6 +1880,10 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "blueprint-build-utils"
+version = "0.1.0"
+
 [[package]]
 name = "blueprint-manager"
 version = "0.1.1"
@@ -5890,6 +5894,7 @@ dependencies = [
  "ark-ff 0.4.2",
  "async-trait",
  "bip39",
+ "blueprint-build-utils",
  "blueprint-test-utils",
  "clap",
  "color-eyre",
@@ -5941,6 +5946,7 @@ dependencies = [
  "ark-ec",
  "ark-ff 0.4.2",
  "async-trait",
+ "blueprint-build-utils",
  "blueprint-metadata",
  "color-eyre",
  "ed25519-zebra 4.0.3",
diff --git a/Cargo.toml b/Cargo.toml
index 6b4dcea3c..9452901c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,7 @@
 [workspace]
 resolver = "2"
 members = [
+    "blueprint-build-utils",
     "blueprint-metadata",
     "blueprints/incredible-squaring",
     "blueprints/incredible-squaring-eigenlayer",
@@ -54,6 +55,7 @@ tangle-raw-event-listener-blueprint = { path = "./blueprints/tangle-raw-event-li
 gadget-blueprint-proc-macro = { path = "./macros/blueprint-proc-macro", default-features = false, version = "0.2.3" }
 gadget-blueprint-proc-macro-core = { path = "./macros/blueprint-proc-macro-core", default-features = false, version = "0.1.5" }
 gadget-context-derive = { path = "./macros/context-derive", default-features = false, version = "0.1.3" }
+blueprint-build-utils = { path = "./blueprint-build-utils", default-features = false, version = "0.1.0" }
 blueprint-metadata = { path = "./blueprint-metadata", default-features = false, version = "0.1.6" }
 cargo-tangle = { path = "./cli", version = "0.2.1" }
 cargo_metadata = { version = "0.18.1" }
diff --git a/blueprint-build-utils/Cargo.toml b/blueprint-build-utils/Cargo.toml
new file mode 100644
index 000000000..939476d1c
--- /dev/null
+++ b/blueprint-build-utils/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "blueprint-build-utils"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+
+[lints]
+workspace = true
diff --git a/blueprint-build-utils/src/lib.rs b/blueprint-build-utils/src/lib.rs
new file mode 100644
index 000000000..6d493d8c6
--- /dev/null
+++ b/blueprint-build-utils/src/lib.rs
@@ -0,0 +1,61 @@
+use std::env;
+use std::path::PathBuf;
+use std::process::Command;
+
+/// Build the Smart contracts at the specified directories, automatically rerunning if changes are
+/// detected in this crates Smart Contracts (`./contracts/lib`).
+///
+/// # Panics
+/// - If the Cargo Manifest directory is not found.
+/// - If the `forge` executable is not found.
+pub fn build_contracts(contract_dirs: Vec<&str>) {
+    println!("cargo::rerun-if-changed=contracts/lib/*");
+    println!("cargo::rerun-if-changed=contracts/src/*");
+
+    // Get the project root directory
+    let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+
+    // Try to find the `forge` executable dynamically
+    let forge_executable = match Command::new("which").arg("forge").output() {
+        Ok(output) => {
+            let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
+            assert!(
+                !path.is_empty(),
+                "Forge executable not found. Make sure Foundry is installed."
+            );
+            path
+        }
+        Err(e) => panic!("Failed to find `forge` executable: {e}"),
+    };
+
+    for dir in contract_dirs {
+        let full_path = root.join(dir).canonicalize().unwrap_or_else(|_| {
+            println!(
+                "Directory not found or inaccessible: {}",
+                root.join(dir).display()
+            );
+            root.join(dir)
+        });
+
+        if full_path.exists() {
+            println!("cargo:rerun-if-changed={}", full_path.display());
+
+            let status = Command::new(&forge_executable)
+                .current_dir(&full_path)
+                .arg("build")
+                .status()
+                .expect("Failed to execute Forge build");
+
+            assert!(
+                status.success(),
+                "Forge build failed for directory: {}",
+                full_path.display()
+            );
+        } else {
+            println!(
+                "Directory not found or does not exist: {}",
+                full_path.display()
+            );
+        }
+    }
+}
diff --git a/blueprints/incredible-squaring-eigenlayer/Cargo.toml b/blueprints/incredible-squaring-eigenlayer/Cargo.toml
index 97a8ee997..5863f598c 100644
--- a/blueprints/incredible-squaring-eigenlayer/Cargo.toml
+++ b/blueprints/incredible-squaring-eigenlayer/Cargo.toml
@@ -59,6 +59,9 @@ num-bigint = { workspace = true }
 blueprint-test-utils = { workspace = true }
 gadget-io = { workspace = true }
 
+[build-dependencies]
+blueprint-build-utils = { workspace = true }
+
 [features]
 default = ["std"]
 std = []
diff --git a/blueprints/incredible-squaring-eigenlayer/build.rs b/blueprints/incredible-squaring-eigenlayer/build.rs
index 122edeefd..e3190ee5f 100644
--- a/blueprints/incredible-squaring-eigenlayer/build.rs
+++ b/blueprints/incredible-squaring-eigenlayer/build.rs
@@ -1,12 +1,4 @@
-use std::env;
-use std::path::PathBuf;
-use std::process::Command;
-
 fn main() {
-    println!("cargo:rerun-if-changed=src/cli");
-    println!("cargo:rerun-if-changed=src/lib.rs");
-    println!("cargo:rerun-if-changed=src/main.rs");
-
     let contract_dirs: Vec<&str> = vec![
         "./contracts/lib/eigenlayer-middleware/lib/eigenlayer-contracts",
         "./contracts/lib/eigenlayer-middleware",
@@ -14,47 +6,5 @@ fn main() {
         "./contracts",
     ];
 
-    // Get the project root directory
-    let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
-
-    // Try to find the `forge` executable dynamically
-    let forge_executable = match Command::new("which").arg("forge").output() {
-        Ok(output) => {
-            let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
-            if path.is_empty() {
-                panic!("Forge executable not found. Make sure Foundry is installed.");
-            }
-            path
-        }
-        Err(_) => panic!("Failed to locate `forge` executable. Make sure Foundry is installed."),
-    };
-
-    for dir in contract_dirs {
-        let full_path = root.join(dir).canonicalize().unwrap_or_else(|_| {
-            println!(
-                "Directory not found or inaccessible: {}",
-                root.join(dir).display()
-            );
-            root.join(dir)
-        });
-
-        if full_path.exists() {
-            println!("cargo:rerun-if-changed={}", full_path.display());
-
-            let status = Command::new(&forge_executable)
-                .current_dir(&full_path)
-                .arg("build")
-                .status()
-                .expect("Failed to execute Forge build");
-
-            if !status.success() {
-                panic!("Forge build failed for directory: {}", full_path.display());
-            }
-        } else {
-            println!(
-                "Directory not found or does not exist: {}",
-                full_path.display()
-            );
-        }
-    }
+    blueprint_build_utils::build_contracts(contract_dirs);
 }
diff --git a/blueprints/incredible-squaring-symbiotic/Cargo.toml b/blueprints/incredible-squaring-symbiotic/Cargo.toml
index 1738451a4..4739088b0 100644
--- a/blueprints/incredible-squaring-symbiotic/Cargo.toml
+++ b/blueprints/incredible-squaring-symbiotic/Cargo.toml
@@ -49,6 +49,7 @@ lazy_static = { workspace = true }
 
 [build-dependencies]
 blueprint-metadata = { workspace = true }
+blueprint-build-utils = { workspace = true }
 
 [features]
 default = ["std"]
diff --git a/blueprints/incredible-squaring-symbiotic/build.rs b/blueprints/incredible-squaring-symbiotic/build.rs
index 0f05d1a01..5a98e538b 100644
--- a/blueprints/incredible-squaring-symbiotic/build.rs
+++ b/blueprints/incredible-squaring-symbiotic/build.rs
@@ -1,59 +1,9 @@
-use std::env;
-use std::path::PathBuf;
-use std::process::Command;
-
 fn main() {
-    println!("cargo:rerun-if-changed=src/cli");
-    println!("cargo:rerun-if-changed=src/lib.rs");
-    println!("cargo:rerun-if-changed=src/main.rs");
-
     let contract_dirs: Vec<&str> = vec![
         "./contracts/lib/core",
         "./contracts/lib/forge-std",
         "./contracts",
     ];
 
-    // Get the project root directory
-    let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
-    print!("root: {:?}", root);
-    // Try to find the `forge` executable dynamically
-    let forge_executable = match Command::new("which").arg("forge").output() {
-        Ok(output) => {
-            let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
-            if path.is_empty() {
-                panic!("Forge executable not found. Make sure Foundry is installed.");
-            }
-            path
-        }
-        Err(_) => panic!("Failed to locate `forge` executable. Make sure Foundry is installed."),
-    };
-
-    for dir in contract_dirs {
-        let full_path = root.join(dir).canonicalize().unwrap_or_else(|_| {
-            println!(
-                "Directory not found or inaccessible: {}",
-                root.join(dir).display()
-            );
-            root.join(dir)
-        });
-
-        if full_path.exists() {
-            println!("cargo:rerun-if-changed={}", full_path.display());
-
-            let status = Command::new(&forge_executable)
-                .current_dir(&full_path)
-                .arg("build")
-                .status()
-                .expect("Failed to execute Forge build");
-
-            if !status.success() {
-                panic!("Forge build failed for directory: {}", full_path.display());
-            }
-        } else {
-            println!(
-                "Directory not found or does not exist: {}",
-                full_path.display()
-            );
-        }
-    }
+    blueprint_build_utils::build_contracts(contract_dirs);
 }


Test patch: