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