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