ab_contracts_tooling/
build.rs1use crate::target_specification::TARGET_SPECIFICATION_NAME;
4use anyhow::Context;
5use cargo_metadata::MetadataCommand;
6use std::env;
7use std::path::{Path, PathBuf};
8use std::process::Command;
9use tracing::debug;
10
11#[derive(Debug)]
13pub struct BuildOptions<'a> {
14 pub package: Option<&'a str>,
18 pub features: Option<&'a str>,
20 pub no_default_features: bool,
22 pub profile: &'a str,
24 pub target_specification_path: &'a Path,
26 pub target_dir: Option<&'a Path>,
28}
29
30pub fn build_cdylib(options: BuildOptions<'_>) -> anyhow::Result<PathBuf> {
32 let BuildOptions {
33 package,
34 features,
35 no_default_features,
36 profile,
37 target_specification_path,
38 target_dir,
39 } = options;
40
41 let mut command_builder = Command::new("cargo");
42 command_builder
43 .env_remove("RUSTFLAGS")
44 .env_remove("CARGO_ENCODED_RUSTFLAGS")
45 .env(
48 "RUSTFLAGS",
49 r#"--cfg sha2_backend="riscv-zknh" --cfg sha2_backend_riscv_zknh="compact""#,
50 )
51 .args([
52 "rustc",
53 "-Zbuild-std=core",
54 "--crate-type",
55 "cdylib",
56 "-Zjson-target-spec",
57 "--target",
58 target_specification_path
59 .to_str()
60 .context("Path to target specification file is not valid UTF-8")?,
61 ]);
62
63 if env::var("MIRI_SYSROOT").is_ok() {
64 command_builder
65 .env_remove("RUSTC")
66 .env_remove("RUSTC_WRAPPER");
67 }
68
69 if let Some(package) = package {
70 command_builder.args([
71 "--package",
72 package,
73 "--features",
74 &format!("{package}/guest"),
75 ]);
76 } else {
77 command_builder.args(["--features", "guest"]);
78 }
79 if let Some(features) = features {
80 command_builder.args(["--features", features]);
81 }
82 if no_default_features {
83 command_builder.arg("--no-default-features");
84 }
85
86 command_builder.args(["--profile", profile]);
87
88 let metadata = MetadataCommand::new()
89 .exec()
90 .context("Failed to fetch cargo metadata")?;
91
92 let target_directory = if let Some(target_dir) = target_dir {
93 command_builder.args([
94 "--target-dir",
95 target_dir
96 .to_str()
97 .context("Path to target directory is not valid UTF-8")?,
98 ]);
99 target_dir
100 } else {
101 metadata.target_directory.as_std_path()
102 };
103
104 let cdylib_path = target_directory
105 .join(TARGET_SPECIFICATION_NAME)
106 .join(if profile == "dev" { "debug" } else { profile })
107 .join({
108 let package_name = if let Some(package) = package {
109 package
110 } else {
111 let current_dir = env::current_dir().context("Failed to get current directory")?;
112 let current_manifest = current_dir.join("Cargo.toml");
113 metadata
114 .packages
115 .iter()
116 .find_map(|package| {
117 if package.manifest_path == current_manifest {
118 Some(&package.name)
119 } else {
120 None
121 }
122 })
123 .context("Failed to find package name")?
124 };
125
126 format!("{}.contract.so", package_name.replace('-', "_"))
127 });
128
129 debug!(
130 ?package,
131 ?features,
132 ?profile,
133 ?target_specification_path,
134 cdylib_path = ?cdylib_path,
135 command = ?command_builder,
136 "Building ELF `cdylib` contract"
137 );
138
139 let status = command_builder
140 .status()
141 .context("Failed to build a contract")?;
142
143 if !status.success() {
144 return Err(anyhow::anyhow!("Failed to build a contract"));
145 }
146
147 Ok(cdylib_path)
148}