microsoft/openvmm

Public

mirrored from https://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
b8910f97c258ca39c8f2e535fbc241ea1e1be3ff

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

flowey/flowey_cli/src/cli/regen.rs

200lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use anyhow::Context;
5use std::collections::BTreeMap;
6use std::path::Path;
7use std::path::PathBuf;
8
9/// Regenerate all pipelines defined in the repo's root `.flowey.toml`
10#[derive(clap::Args)]
11pub struct Regen {
12 /// Check that pipelines are up to date, without regenerating them.
13 #[clap(long)]
14 check: bool,
15
16 /// Pass `--quiet` to any subprocess invocations of `cargo run`.
17 #[clap(long)]
18 quiet: bool,
19}
20
21impl Regen {
22 pub fn run(self, repo_root: &Path) -> anyhow::Result<()> {
23 install_flowey_merge_driver()?;
24
25 if !repo_root.join(".flowey.toml").exists() {
26 log::warn!("no .flowey.toml exists in the repo root");
27 return Ok(());
28 }
29
30 let flowey_toml = fs_err::read_to_string(repo_root.join(".flowey.toml"))?;
31 let flowey_toml: flowey_toml::FloweyToml =
32 toml_edit::de::from_str(&flowey_toml).context("while parsing .flowey.toml")?;
33
34 let data = resolve_flowey_toml(flowey_toml, repo_root.to_owned())
35 .context("while resolving .flowey.toml")?;
36
37 let mut bin2flowey = BTreeMap::<String, PathBuf>::new();
38
39 let mut error = false;
40 for ResolvedFloweyToml {
41 working_dir,
42 pipelines,
43 } in data
44 {
45 for (bin_name, pipelines) in pipelines {
46 let exe_name = format!("{bin_name}{}", std::env::consts::EXE_SUFFIX);
47
48 let bin = if let Some(bin) = bin2flowey.get(&bin_name) {
49 bin.clone()
50 } else {
51 // build the requested flowey
52 {
53 let quiet = self.quiet.then_some("-q");
54 let sh = xshell::Shell::new()?;
55 sh.change_dir(&working_dir);
56 xshell::cmd!(sh, "cargo build -p {bin_name} --profile flowey {quiet...}")
57 .run()?;
58 }
59
60 // find the built flowey
61 let bin = working_dir
62 .join(
63 std::env::var("CARGO_TARGET_DIR")
64 .as_deref()
65 .unwrap_or("target"),
66 )
67 .join(std::env::var("CARGO_BUILD_TARGET").as_deref().unwrap_or(""))
68 .join("flowey")
69 .join(&exe_name);
70
71 if !bin.exists() {
72 panic!("should have found built {bin_name}");
73 }
74
75 // stash result for future consumers
76 bin2flowey.insert(bin_name.clone(), bin.clone());
77 bin
78 };
79
80 for (backend, defns) in pipelines {
81 for flowey_toml::PipelineDefn { file, cmd } in defns {
82 let check = if self.check {
83 vec!["--check".into(), file.display().to_string()]
84 } else {
85 vec![]
86 };
87
88 let sh = xshell::Shell::new()?;
89 sh.change_dir(&working_dir);
90 let res = xshell::cmd!(
91 sh,
92 "{bin} pipeline {backend} --out {file} {check...} {cmd...}"
93 )
94 .run();
95
96 if res.is_err() {
97 error = true;
98 }
99 }
100 }
101 }
102 }
103
104 if error {
105 anyhow::bail!("encountered one or more errors")
106 }
107
108 Ok(())
109 }
110}
111
112#[derive(Debug)]
113pub struct ResolvedFloweyToml {
114 pub working_dir: PathBuf,
115 // (bin, (backend, metadata))
116 pub pipelines: BTreeMap<String, BTreeMap<String, Vec<flowey_toml::PipelineDefn>>>,
117}
118
119fn resolve_flowey_toml(
120 flowey_toml: flowey_toml::FloweyToml,
121 working_dir: PathBuf,
122) -> anyhow::Result<Vec<ResolvedFloweyToml>> {
123 let mut v = Vec::new();
124 resolve_flowey_toml_inner(flowey_toml, working_dir, &mut v)?;
125 Ok(v)
126}
127
128fn resolve_flowey_toml_inner(
129 flowey_toml: flowey_toml::FloweyToml,
130 working_dir: PathBuf,
131 resolved: &mut Vec<ResolvedFloweyToml>,
132) -> anyhow::Result<()> {
133 let flowey_toml::FloweyToml { include, pipeline } = flowey_toml;
134
135 let mut resolved_pipelines: BTreeMap<String, BTreeMap<String, Vec<_>>> = BTreeMap::new();
136 for (bin_name, pipelines) in pipeline {
137 for (backend, defns) in pipelines {
138 resolved_pipelines
139 .entry(bin_name.clone())
140 .or_default()
141 .entry(backend)
142 .or_default()
143 .extend(defns);
144 }
145 }
146
147 for path in include.unwrap_or_default() {
148 let path = working_dir.join(path);
149 let flowey_toml = fs_err::read_to_string(&path)?;
150 let flowey_toml: flowey_toml::FloweyToml = toml_edit::de::from_str(&flowey_toml)
151 .with_context(|| anyhow::anyhow!("while parsing {}", path.display()))?;
152 let mut working_dir = path;
153 working_dir.pop();
154 resolve_flowey_toml_inner(flowey_toml, working_dir, resolved)?
155 }
156
157 resolved.push(ResolvedFloweyToml {
158 working_dir,
159 pipelines: resolved_pipelines,
160 });
161
162 Ok(())
163}
164
165mod flowey_toml {
166 use serde::Deserialize;
167 use serde::Serialize;
168 use std::collections::BTreeMap;
169 use std::path::PathBuf;
170
171 #[derive(Debug, Serialize, Deserialize)]
172 pub struct FloweyToml {
173 pub include: Option<Vec<PathBuf>>,
174 // (bin, (backend, metadata))
175 pub pipeline: BTreeMap<String, BTreeMap<String, Vec<PipelineDefn>>>,
176 }
177
178 #[derive(Debug, Serialize, Deserialize)]
179 pub struct PipelineDefn {
180 pub file: PathBuf,
181 pub cmd: Vec<String>,
182 }
183}
184
185fn install_flowey_merge_driver() -> anyhow::Result<()> {
186 const DRIVER_NAME: &str = "flowey-theirs merge driver";
187 const DRIVER_COMMAND: &str = "cp %B %A";
188
189 let sh = xshell::Shell::new()?;
190 xshell::cmd!(sh, "git config merge.flowey-theirs.name {DRIVER_NAME}")
191 .quiet()
192 .ignore_status()
193 .run()?;
194 xshell::cmd!(sh, "git config merge.flowey-theirs.driver {DRIVER_COMMAND}")
195 .quiet()
196 .ignore_status()
197 .run()?;
198
199 Ok(())
200}
201