cargo/core/compiler/
build_plan.rs
1use std::collections::BTreeMap;
10use std::path::{Path, PathBuf};
11
12use serde::Serialize;
13
14use super::build_runner::OutputFile;
15use super::{BuildRunner, CompileKind, CompileMode, Unit};
16use crate::core::TargetKind;
17use crate::util::{internal, CargoResult, GlobalContext};
18use cargo_util::ProcessBuilder;
19
20#[derive(Debug, Serialize)]
21struct Invocation {
22 package_name: String,
23 package_version: semver::Version,
24 target_kind: TargetKind,
25 kind: CompileKind,
26 compile_mode: CompileMode,
27 deps: Vec<usize>,
28 outputs: Vec<PathBuf>,
29 links: BTreeMap<PathBuf, PathBuf>,
30 program: String,
31 args: Vec<String>,
32 env: BTreeMap<String, String>,
33 cwd: Option<PathBuf>,
34}
35
36#[derive(Debug)]
37pub struct BuildPlan {
38 invocation_map: BTreeMap<String, usize>,
39 plan: SerializedBuildPlan,
40}
41
42#[derive(Debug, Serialize)]
43struct SerializedBuildPlan {
44 invocations: Vec<Invocation>,
45 inputs: Vec<PathBuf>,
46}
47
48impl Invocation {
49 pub fn new(unit: &Unit, deps: Vec<usize>) -> Invocation {
50 let id = unit.pkg.package_id();
51 Invocation {
52 package_name: id.name().to_string(),
53 package_version: id.version().clone(),
54 kind: unit.kind,
55 target_kind: unit.target.kind().clone(),
56 compile_mode: unit.mode,
57 deps,
58 outputs: Vec::new(),
59 links: BTreeMap::new(),
60 program: String::new(),
61 args: Vec::new(),
62 env: BTreeMap::new(),
63 cwd: None,
64 }
65 }
66
67 pub fn add_output(&mut self, path: &Path, link: &Option<PathBuf>) {
68 self.outputs.push(path.to_path_buf());
69 if let Some(ref link) = *link {
70 self.links.insert(link.clone(), path.to_path_buf());
71 }
72 }
73
74 pub fn update_cmd(&mut self, cmd: &ProcessBuilder) -> CargoResult<()> {
75 self.program = cmd
76 .get_program()
77 .to_str()
78 .ok_or_else(|| anyhow::format_err!("unicode program string required"))?
79 .to_string();
80 self.cwd = Some(cmd.get_cwd().unwrap().to_path_buf());
81 for arg in cmd.get_args() {
82 self.args.push(
83 arg.to_str()
84 .ok_or_else(|| anyhow::format_err!("unicode argument string required"))?
85 .to_string(),
86 );
87 }
88 for (var, value) in cmd.get_envs() {
89 let Some(value) = value else { continue };
90 self.env.insert(
91 var.clone(),
92 value
93 .to_str()
94 .ok_or_else(|| anyhow::format_err!("unicode environment value required"))?
95 .to_string(),
96 );
97 }
98 Ok(())
99 }
100}
101
102impl BuildPlan {
103 pub fn new() -> BuildPlan {
104 BuildPlan {
105 invocation_map: BTreeMap::new(),
106 plan: SerializedBuildPlan::new(),
107 }
108 }
109
110 pub fn add(&mut self, build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
111 let id = self.plan.invocations.len();
112 self.invocation_map.insert(unit.buildkey(), id);
113 let deps = build_runner
114 .unit_deps(unit)
115 .iter()
116 .map(|dep| self.invocation_map[&dep.unit.buildkey()])
117 .collect();
118 let invocation = Invocation::new(unit, deps);
119 self.plan.invocations.push(invocation);
120 Ok(())
121 }
122
123 pub fn update(
124 &mut self,
125 invocation_name: &str,
126 cmd: &ProcessBuilder,
127 outputs: &[OutputFile],
128 ) -> CargoResult<()> {
129 let id = self.invocation_map[invocation_name];
130 let invocation =
131 self.plan.invocations.get_mut(id).ok_or_else(|| {
132 internal(format!("couldn't find invocation for {}", invocation_name))
133 })?;
134
135 invocation.update_cmd(cmd)?;
136 for output in outputs.iter() {
137 invocation.add_output(&output.path, &output.hardlink);
138 }
139
140 Ok(())
141 }
142
143 pub fn set_inputs(&mut self, inputs: Vec<PathBuf>) {
144 self.plan.inputs = inputs;
145 }
146
147 pub fn output_plan(self, gctx: &GlobalContext) {
148 let encoded = serde_json::to_string(&self.plan).unwrap();
149 crate::drop_println!(gctx, "{}", encoded);
150 }
151}
152
153impl SerializedBuildPlan {
154 pub fn new() -> SerializedBuildPlan {
155 SerializedBuildPlan {
156 invocations: Vec::new(),
157 inputs: Vec::new(),
158 }
159 }
160}