ink_e2e/
contract_build.rs1use crate::log_info;
16use contract_build::{
17 BuildArtifacts,
18 BuildMode,
19 ExecuteArgs,
20 Features,
21 ImageVariant,
22 ManifestPath,
23 MetadataSpec,
24 Network,
25 OutputType,
26 UnstableFlags,
27 Verbosity,
28};
29use itertools::Itertools;
30use std::{
31 collections::{
32 hash_map::Entry,
33 HashMap,
34 },
35 path::{
36 Path,
37 PathBuf,
38 },
39 sync::{
40 Mutex,
41 OnceLock,
42 },
43};
44
45pub fn build_root_and_contract_dependencies() -> Vec<PathBuf> {
48 let contract_project = ContractProject::new();
49 let contract_manifests = contract_project.root_with_contract_dependencies();
50 build_contracts(&contract_manifests)
51}
52
53struct ContractProject {
56 root_package: Option<PathBuf>,
57 contract_dependencies: Vec<PathBuf>,
58}
59
60impl ContractProject {
61 fn new() -> Self {
62 let cmd = cargo_metadata::MetadataCommand::new();
63 let metadata = cmd
64 .exec()
65 .unwrap_or_else(|err| panic!("Error invoking `cargo metadata`: {}", err));
66
67 fn maybe_contract_package(package: &cargo_metadata::Package) -> Option<PathBuf> {
68 package
69 .features
70 .iter()
71 .any(|(feat, _)| {
72 feat == "ink-as-dependency"
73 && !package.name.eq("ink")
74 && !package.name.eq("ink_env")
75 })
76 .then(|| package.manifest_path.clone().into_std_path_buf())
77 }
78
79 let root_package = metadata
80 .resolve
81 .as_ref()
82 .and_then(|resolve| resolve.root.as_ref())
83 .and_then(|root_package_id| {
84 metadata
85 .packages
86 .iter()
87 .find(|package| &package.id == root_package_id)
88 })
89 .and_then(maybe_contract_package);
90 log_info(&format!("found root package: {:?}", root_package));
91
92 let contract_dependencies: Vec<PathBuf> = metadata
93 .packages
94 .iter()
95 .filter_map(maybe_contract_package)
96 .collect();
97
98 log_info(&format!(
99 "found those contract dependencies: {:?}",
100 contract_dependencies
101 ));
102 Self {
103 root_package,
104 contract_dependencies,
105 }
106 }
107
108 fn root_with_additional_contracts<P>(
109 &self,
110 additional_contracts: impl IntoIterator<Item = P>,
111 ) -> Vec<PathBuf>
112 where
113 PathBuf: From<P>,
114 {
115 let mut all_manifests: Vec<_> = self.root_package.iter().cloned().collect();
116 let mut additional_contracts: Vec<_> = additional_contracts
117 .into_iter()
118 .map(PathBuf::from)
119 .collect();
120 all_manifests.append(&mut additional_contracts);
121 all_manifests.into_iter().unique().collect()
122 }
123
124 fn root_with_contract_dependencies(&self) -> Vec<PathBuf> {
125 self.root_with_additional_contracts(&self.contract_dependencies)
126 }
127}
128
129fn build_contracts(contract_manifests: &[PathBuf]) -> Vec<PathBuf> {
134 static CONTRACT_BUILD_JOBS: OnceLock<Mutex<HashMap<PathBuf, PathBuf>>> =
135 OnceLock::new();
136 let mut contract_build_jobs = CONTRACT_BUILD_JOBS
137 .get_or_init(|| Mutex::new(HashMap::new()))
138 .lock()
139 .unwrap();
140
141 let mut blob_paths = Vec::new();
142 for manifest in contract_manifests {
143 let contract_binary_path = match contract_build_jobs.entry(manifest.clone()) {
144 Entry::Occupied(entry) => entry.get().clone(),
145 Entry::Vacant(entry) => {
146 let contract_binary_path = build_contract(manifest);
147 entry.insert(contract_binary_path.clone());
148 contract_binary_path
149 }
150 };
151 blob_paths.push(contract_binary_path);
152 }
153 blob_paths
154}
155
156fn build_contract(path_to_cargo_toml: &Path) -> PathBuf {
159 let manifest_path = ManifestPath::new(path_to_cargo_toml).unwrap_or_else(|err| {
160 panic!(
161 "Invalid manifest path {}: {err}",
162 path_to_cargo_toml.display()
163 )
164 });
165 let args = ExecuteArgs {
166 manifest_path,
167 verbosity: Verbosity::Default,
168 build_mode: BuildMode::Release, features: Features::default(),
170 network: Network::Online,
171 build_artifact: BuildArtifacts::All,
172 unstable_flags: UnstableFlags::default(),
173 keep_debug_symbols: false,
174 extra_lints: false,
175 output_type: OutputType::HumanReadable,
176 skip_clippy_and_linting: false,
177 image: ImageVariant::Default,
178 metadata_spec: MetadataSpec::Ink,
179 };
180
181 match contract_build::execute(args) {
182 Ok(build_result) => {
183 build_result
184 .dest_binary
185 .expect("PolkaVM code artifact not generated")
186 .canonicalize()
187 .expect("Invalid dest bundle path")
188 }
189 Err(err) => {
190 panic!(
191 "contract build for {} failed: {err}",
192 path_to_cargo_toml.display()
193 )
194 }
195 }
196}