1use std::{
2 any::Any,
3 num::NonZeroU32,
4 path::{Path, PathBuf},
5};
6
7use either::Either;
8use flams_ontology::uris::{
9 ArchiveId, ArchiveURI, ArchiveURIRef, ArchiveURITrait, DocumentURI, ModuleURI, URIRefTrait,
10};
11use flams_utils::{
12 time::Eta,
13 triomphe::Arc,
14 vecmap::{VecMap, VecSet},
15};
16use parking_lot::RwLock;
17
18use crate::formats::{BuildArtifactTypeId, BuildTargetId};
19
20mod queue;
21pub mod queue_manager;
22pub use queue::QueueName;
23mod queueing;
24
25lazy_static::lazy_static! {
26 pub(crate) static ref BUILD_QUEUE_SPAN:tracing::Span = {
27 tracing::info_span!(target:"build queue",parent:None,"Build Queue")
29 };
30}
31
32#[cfg(all(test, feature = "tokio"))]
33mod tests;
34
35pub use queue::Queue;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
38pub enum TaskState {
39 Running,
40 Queued,
41 Blocked,
42 Done,
43 Failed,
44 None,
45}
46
47#[derive(Debug, Clone, Hash, PartialEq, Eq)]
48pub struct TaskRef {
49 pub archive: ArchiveId,
50 pub rel_path: std::sync::Arc<str>,
51 pub target: BuildTargetId,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum Dependency {
56 Physical {
57 task: TaskRef,
58 strict: bool,
59 },
60 Logical {
61 uri: ModuleURI,
62 strict: bool,
63 },
64 Resolved {
65 task: BuildTask,
66 step: BuildTargetId,
67 strict: bool,
68 },
69}
70
71#[derive(Copy, Clone, Debug, PartialEq, Eq)]
72pub struct BuildTaskId(NonZeroU32);
73impl From<BuildTaskId> for u32 {
74 #[inline]
75 fn from(id: BuildTaskId) -> Self {
76 id.0.get()
77 }
78}
79
80#[derive(Debug, PartialEq, Eq)]
81struct BuildTaskI {
82 id: BuildTaskId,
83 archive: ArchiveURI,
84 steps: Box<[BuildStep]>,
85 source: Either<PathBuf, String>,
86 rel_path: std::sync::Arc<str>,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct BuildTask(Arc<BuildTaskI>);
91impl BuildTask {
92 #[must_use]
93 #[inline]
94 pub fn document_uri(&self) -> eyre::Result<DocumentURI> {
95 DocumentURI::from_archive_relpath(self.archive().owned(), self.rel_path())
96 }
97 #[must_use]
98 pub fn get_task_ref(&self, target: BuildTargetId) -> TaskRef {
99 TaskRef {
100 archive: self.0.archive.archive_id().clone(),
101 rel_path: self.0.rel_path.clone(),
102 target,
103 }
104 }
105
106 #[inline]
107 #[must_use]
108 pub fn source(&self) -> Either<&Path, &str> {
109 match &self.0.source {
110 Either::Left(p) => Either::Left(p),
111 Either::Right(s) => Either::Right(s),
112 }
113 }
114
115 #[inline]
116 #[must_use]
117 pub fn archive(&self) -> ArchiveURIRef {
118 self.0.archive.archive_uri()
119 }
120
121 #[inline]
122 #[must_use]
123 pub fn rel_path(&self) -> &str {
124 &self.0.rel_path
125 }
126
127 #[inline]
128 #[must_use]
129 pub fn steps(&self) -> &[BuildStep] {
130 &self.0.steps
131 }
132
133 #[inline]
134 #[must_use]
135 pub fn get_step(&self, target: BuildTargetId) -> Option<&BuildStep> {
136 self.0.steps.iter().find(|s| s.0.target == target)
137 }
138
139 #[must_use]
140 #[allow(clippy::cast_possible_truncation)]
141 pub fn as_message(&self) -> QueueEntry {
142 QueueEntry {
148 id: self.0.id,
149 archive: self.0.archive.archive_id().clone(),
150 rel_path: self.0.rel_path.clone(),
151 steps: self
152 .steps()
153 .iter()
154 .map(|s| (s.0.target, *s.0.state.read()))
155 .collect(),
156 }
157 }
158}
159
160#[derive(Debug)]
161struct BuildStepI {
162 target: BuildTargetId,
164 state: RwLock<TaskState>,
165 requires: RwLock<VecSet<Dependency>>,
167 dependents: RwLock<Vec<(BuildTaskId, BuildTargetId)>>,
168}
169impl PartialEq for BuildStepI {
170 fn eq(&self, other: &Self) -> bool {
171 self.target == other.target
172 }
173}
174impl Eq for BuildStepI {}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
177pub struct BuildStep(Arc<BuildStepI>);
178impl BuildStep {
179 pub fn add_dependency(&self, dep: Dependency) {
180 self.0.requires.write().insert(dep);
181 }
182 }
189
190pub trait BuildArtifact: Any + 'static {
191 fn get_type_id() -> BuildArtifactTypeId
192 where
193 Self: Sized;
194 fn load(p: &Path) -> Result<Self, std::io::Error>
196 where
197 Self: Sized;
198 fn get_type(&self) -> BuildArtifactTypeId;
199 fn write(&self, path: &Path) -> Result<(), std::io::Error>;
201 fn as_any(&self) -> &dyn Any;
202}
203
204pub enum BuildResultArtifact {
205 File(BuildArtifactTypeId, PathBuf),
206 Data(Box<dyn BuildArtifact>),
207 None,
208}
209
210pub struct BuildResult {
211 pub log: Either<String, PathBuf>,
212 pub result: Result<BuildResultArtifact, Vec<Dependency>>,
213}
214impl BuildResult {
215 #[must_use]
216 #[inline]
217 pub const fn empty() -> Self {
218 Self {
219 log: Either::Left(String::new()),
220 result: Ok(BuildResultArtifact::None),
221 }
222 }
223 #[must_use]
224 #[inline]
225 pub const fn err() -> Self {
226 Self {
227 log: Either::Left(String::new()),
228 result: Err(Vec::new()),
229 }
230 }
231
232 #[inline]
233 pub fn with_err(s: String) -> Self {
234 Self {
235 log: Either::Left(s),
236 result: Err(Vec::new()),
237 }
238 }
239}
240
241#[derive(Debug, Clone)]
242pub struct QueueEntry {
243 pub id: BuildTaskId,
244 pub archive: ArchiveId,
245 pub rel_path: std::sync::Arc<str>,
246 pub steps: VecMap<BuildTargetId, TaskState>,
247}
248
249#[derive(Debug, Clone)]
250pub enum QueueMessage {
251 Idle(Vec<QueueEntry>),
252 Started {
253 running: Vec<QueueEntry>,
254 queue: Vec<QueueEntry>,
255 blocked: Vec<QueueEntry>,
256 failed: Vec<QueueEntry>,
257 done: Vec<QueueEntry>,
258 },
259 Finished {
260 failed: Vec<QueueEntry>,
261 done: Vec<QueueEntry>,
262 },
263 TaskStarted {
264 id: BuildTaskId,
265 target: BuildTargetId,
266 },
267 TaskSuccess {
268 id: BuildTaskId,
269 target: BuildTargetId,
270 eta: Eta,
271 },
272 TaskFailed {
273 id: BuildTaskId,
274 target: BuildTargetId,
275 eta: Eta,
276 },
277}