ic_build/cargo/observation/
mod.rs1#[cfg(test)]
8mod tests;
9
10mod workspace;
11pub use workspace::workspace_root_for;
12
13pub use cargo_metadata as schema;
15
16use ic_host_process::tool::{CompletedExecution, OutputLimits, capture_command};
17use serde::Deserialize;
18use serde_json::Value;
19use std::{collections::BTreeSet, path::Path, process::Command};
20
21pub(crate) fn decode_metadata(value: &Value) -> Result<schema::Metadata, serde_json::Error> {
24 schema::Metadata::deserialize(value)
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct CargoFeatureSelection {
30 pub features: BTreeSet<String>,
32 pub default_features: bool,
34}
35
36impl Default for CargoFeatureSelection {
37 fn default() -> Self {
38 Self {
39 features: BTreeSet::new(),
40 default_features: true,
41 }
42 }
43}
44
45impl CargoFeatureSelection {
46 fn configure(&self, command: &mut Command) {
47 if !self.default_features {
48 command.arg("--no-default-features");
49 }
50 if !self.features.is_empty() {
51 let mut features = String::new();
52 for (index, feature) in self.features.iter().enumerate() {
53 if index > 0 {
54 features.push(',');
55 }
56 features.push_str(feature);
57 }
58 command.arg("--features").arg(features);
59 }
60 }
61}
62
63pub struct CargoMetadataSpec<'a> {
65 pub manifest_path: &'a Path,
67 pub include_dependencies: bool,
69 pub filter_platform: Option<&'a str>,
71 pub locked: bool,
73 pub offline: bool,
75 pub features: &'a CargoFeatureSelection,
77}
78
79pub struct CargoTreeSpec<'a> {
81 pub manifest_path: &'a Path,
83 pub packages: &'a [&'a str],
85 pub target: &'a str,
87 pub edges: &'a str,
89 pub format: &'a str,
91 pub locked: bool,
93 pub offline: bool,
95 pub features: &'a CargoFeatureSelection,
97}
98
99pub fn observe_metadata(
110 spec: &CargoMetadataSpec<'_>,
111 limits: OutputLimits,
112 mut command: Command,
113) -> Result<schema::Metadata, Box<dyn std::error::Error>> {
114 command.args(["metadata", "--format-version=1"]);
115 configure(
116 &mut command,
117 spec.manifest_path,
118 spec.locked,
119 spec.offline,
120 spec.features,
121 )?;
122 if !spec.include_dependencies {
123 command.arg("--no-deps");
124 }
125 if let Some(target) = spec.filter_platform {
126 command.arg("--filter-platform").arg(target);
127 }
128 let output = capture(&mut command, limits)?;
129 Ok(serde_json::from_slice(&output.stdout)?)
130}
131
132pub fn observe_tree(
141 spec: &CargoTreeSpec<'_>,
142 limits: OutputLimits,
143 mut command: Command,
144) -> Result<String, Box<dyn std::error::Error>> {
145 if spec.packages.is_empty() || spec.packages.iter().any(|package| package.is_empty()) {
146 return Err("Cargo tree requires nonempty package selectors".into());
147 }
148 command.args([
149 "tree",
150 "--color",
151 "never",
152 "--target",
153 spec.target,
154 "--edges",
155 spec.edges,
156 "--prefix",
157 "depth",
158 "--no-dedupe",
159 "--charset",
160 "ascii",
161 "--format",
162 spec.format,
163 ]);
164 configure(
165 &mut command,
166 spec.manifest_path,
167 spec.locked,
168 spec.offline,
169 spec.features,
170 )?;
171 for package in spec.packages {
172 command.arg("--package").arg(package);
173 }
174 let output = capture(&mut command, limits)?;
175 Ok(String::from_utf8(output.stdout)?)
176}
177
178fn configure(
179 command: &mut Command,
180 manifest: &Path,
181 locked: bool,
182 offline: bool,
183 features: &CargoFeatureSelection,
184) -> std::io::Result<()> {
185 let manifest = if manifest.is_absolute() {
186 manifest.to_path_buf()
187 } else {
188 std::env::current_dir()?.join(manifest)
189 };
190 command.current_dir(manifest.parent().unwrap_or_else(|| Path::new(".")));
191 command.arg("--manifest-path").arg(manifest);
192 features.configure(command);
193 if locked {
194 command.arg("--locked");
195 }
196 if offline {
197 command.arg("--offline");
198 }
199 Ok(())
200}
201
202fn capture(
203 command: &mut Command,
204 limits: OutputLimits,
205) -> Result<CompletedExecution, ic_host_process::tool::ToolError> {
206 capture_command(command, limits)
207 .and_then(CompletedExecution::require_success)
208 .and_then(CompletedExecution::require_complete)
209}