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