1use super::Task;
2use crate::defaults::tasks::Empty;
3use crate::exception::BuildException;
4use crate::identifier::TaskId;
5use crate::project::buildable::{BuiltByContainer, IntoBuildable};
6use crate::project::error::{ProjectError, ProjectResult};
7use crate::project::shared::WeakSharedProject;
8use crate::task::action::{Action, TaskAction};
9use crate::task::flags::{OptionDeclarations, OptionsDecoder};
10use crate::task::task_io::TaskIO;
11use crate::task::up_to_date::{UpToDate, UpToDateContainer};
12
13use crate::task::work_handler::WorkHandler;
14use crate::task::{BuildableTask, ExecutableTask, HasTaskId, TaskOrdering, TaskOrderingKind};
15use crate::{BuildResult, Project};
16
17use log::{debug, error, trace};
18
19use std::fmt::{Debug, Formatter};
20
21use std::ops::{Deref, DerefMut};
22
23use crate::error::PayloadError;
24use crate::project::shared::SharedProject;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::sync::Mutex;
27
28pub struct Executable<T: Task> {
30 pub task: T,
31 project: WeakSharedProject,
32 task_id: TaskId,
33 first: Mutex<Vec<Action<T>>>,
34 last: Mutex<Vec<Action<T>>>,
35 task_ordering: Vec<TaskOrdering>,
36 queried: AtomicBool,
37 up_to_date: UpToDateContainer<T>,
38 work: WorkHandler,
39
40 description: String,
41 group: String,
42}
43
44assert_impl_all!(Executable<Empty> : Send);
45
46impl<T: 'static + Task + Send + Debug> Executable<T> {
47 pub fn new<Id: AsRef<TaskId>>(shared: SharedProject, task: T, task_id: Id) -> Self {
48 let cache_location = shared
49 .with(|p| p.root_dir())
50 .join(".assemble")
51 .join("task-cache");
52 debug!(
53 "Using {:?} as cache location for {}",
54 cache_location, shared
55 );
56 let id = task_id.as_ref().clone();
57
58 Self {
59 task,
60 project: shared.weak(),
61 task_id: id.clone(),
62 first: Default::default(),
63 last: Default::default(),
64 task_ordering: Default::default(),
65 queried: AtomicBool::new(false),
66 up_to_date: UpToDateContainer::default(),
67 work: WorkHandler::new(&id, cache_location),
68 description: T::description(),
69 group: "".to_string(),
70 }
71 }
72
73 pub fn initialize(&mut self, project: &Project) -> ProjectResult {
75 T::initialize(self, project)
76 }
77
78 pub fn configure_io(&mut self) -> ProjectResult {
80 T::configure_io(self)
81 }
82
83 pub fn depends_on<B: IntoBuildable>(&mut self, buildable: B)
84 where
85 B::Buildable: 'static,
86 {
87 trace!("adding depends ordering for {:?}", self);
88 let buildable = TaskOrdering::depends_on(buildable);
89 self.task_ordering.push(buildable);
90 }
91
92 pub fn do_first<F>(&mut self, a: F) -> ProjectResult
93 where
94 F: Fn(&mut Executable<T>, &Project) -> BuildResult + 'static,
95 F: Send + Sync,
96 {
97 let action = Action::new(a);
98 self.first.lock().map_err(PayloadError::new)?.push(action);
99 Ok(())
100 }
101
102 pub fn do_last<F>(&mut self, a: F) -> ProjectResult
103 where
104 F: Fn(&mut Executable<T>, &Project) -> BuildResult + 'static,
105 F: Send + Sync,
106 {
107 let action = Action::new(a);
108 self.last.lock().map_err(PayloadError::new)?.push(action);
109 Ok(())
110 }
111
112 fn query_actions(&self) -> ProjectResult<(Vec<Action<T>>, Vec<Action<T>>)> {
113 match self
114 .queried
115 .compare_exchange(false, true, Ordering::Release, Ordering::Relaxed)
116 {
117 Ok(false) => {
118 let first: Vec<_> = self
119 .first
120 .lock()
121 .map_err(PayloadError::new)?
122 .drain(..)
123 .rev()
124 .collect();
125 let last: Vec<_> = self
126 .last
127 .lock()
128 .map_err(PayloadError::new)?
129 .drain(..)
130 .collect();
131 Ok((first, last))
132 }
133 Ok(true) => unreachable!(),
134 Err(_) => Err(ProjectError::ActionsAlreadyQueried.into()),
135 }
136 }
137
138 fn actions(&self) -> ProjectResult<Vec<Box<dyn TaskAction<T>>>> {
139 let mut output: Vec<Box<dyn TaskAction<T>>> = vec![];
140 let (first, last) = self.query_actions()?;
141 output.extend(
142 first
143 .into_iter()
144 .map(|a| Box::new(a) as Box<dyn TaskAction<T>>),
145 );
146 output.push(Box::new(T::task_action));
147 output.extend(
148 last.into_iter()
149 .map(|a| Box::new(a) as Box<dyn TaskAction<T>>),
150 );
151 Ok(output)
152 }
153 pub fn project(&self) -> SharedProject {
154 SharedProject::try_from(self.project.clone()).unwrap()
155 }
156
157 pub fn work(&mut self) -> &mut WorkHandler {
158 &mut self.work
159 }
160
161 fn handler_up_to_date(&self) -> bool {
162 if !self.task.up_to_date() {
163 return false;
164 }
165 let handler = self.up_to_date.handler(self);
166 handler.up_to_date()
167 }
168
169 pub fn set_description(&mut self, description: &str) {
170 self.description = description.to_string();
171 }
172 pub fn set_group(&mut self, group: &str) {
173 self.group = group.to_string();
174 }
175
176 fn up_to_date_before_execution(&self) -> ProjectResult<bool> {
180 if self.up_to_date.len() > 0 && self.handler_up_to_date() {
181 return Ok(true);
182 }
183 if !UpToDate::up_to_date(&self.task) {
184 return Ok(false);
185 }
186 match self.work.prev_work() {
187 None => Ok(false),
188 Some((prev_i, prev_o)) => {
189 if !self.handler_up_to_date() {
191 return Ok(false);
192 }
193
194 let current_i = self.work.get_input()?;
196 if current_i.input_changed(Some(prev_i)) {
197 debug!("{} not up-to-date because input has changed", self.task_id);
198 return Ok(false);
199 }
200
201 Ok(if prev_o.up_to_date() {
203 true
204 } else {
205 debug!("{} not up-to-date because output has changed", self.task_id);
206 false
207 })
208 }
209 }
210 }
211
212 pub fn up_to_date<F: Fn(&Executable<T>) -> bool + Send + Sync + 'static>(
214 &mut self,
215 configure: F,
216 ) {
217 self.up_to_date.up_to_date_if(configure)
218 }
219}
220
221impl<T: Task + Debug> Debug for Executable<T> {
222 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
223 f.debug_struct("Executable")
224 .field("id", &self.task_id)
225 .field("task", &self.task)
226 .field("ordering", &self.task_ordering)
227 .finish_non_exhaustive()
228 }
229}
230
231impl<T: Task> Deref for Executable<T> {
232 type Target = T;
233
234 fn deref(&self) -> &Self::Target {
235 &self.task
236 }
237}
238
239impl<T: Task> DerefMut for Executable<T> {
240 fn deref_mut(&mut self) -> &mut Self::Target {
241 &mut self.task
242 }
243}
244
245impl<T: Task + Send + Debug> IntoBuildable for &Executable<T> {
246 type Buildable = BuiltByContainer;
247
248 fn into_buildable(self) -> Self::Buildable {
249 trace!("Creating BuiltByContainer for {:?}", self);
250 let mut built_by = BuiltByContainer::new();
251 built_by.add(self.task_id.clone());
252 for ordering in self
253 .task_ordering
254 .iter()
255 .filter(|b| b.ordering_kind() == &TaskOrderingKind::DependsOn)
256 {
257 built_by.add(ordering.buildable().clone());
258 }
259 built_by.add(self.work.into_buildable());
260 built_by
261 }
262}
263
264impl<T: 'static + Task + Send + Debug> HasTaskId for Executable<T> {
265 fn task_id(&self) -> TaskId {
266 self.task_id.clone()
267 }
268}
269
270impl<T: 'static + Task + Send + Debug> BuildableTask for Executable<T> {
271 fn ordering(&self) -> Vec<TaskOrdering> {
272 let mut explicit = self.task_ordering.clone();
273 let inputs = self.work.into_buildable();
274 let inputs_ordering = TaskOrdering::depends_on(inputs);
275 explicit.push(inputs_ordering);
276 explicit
277 }
278}
279
280pub static FORCE_RERUN: AtomicBool = AtomicBool::new(false);
282pub fn force_rerun(value: bool) {
283 FORCE_RERUN.store(value, Ordering::Relaxed)
284}
285
286impl<T: 'static + Task + Send + Sync + Debug> ExecutableTask for Executable<T> {
287 fn options_declarations(&self) -> Option<OptionDeclarations> {
288 T::options_declarations()
289 }
290
291 fn try_set_from_decoder(&mut self, decoder: &OptionsDecoder) -> ProjectResult<()> {
292 self.task.try_set_from_decoder(decoder)
293 }
294
295 fn execute(&mut self, project: &Project) -> BuildResult {
296 let up_to_date = if FORCE_RERUN.load(Ordering::Relaxed) {
297 false
298 } else {
299 self.up_to_date_before_execution()?
300 };
301
302 let work = if !up_to_date {
303 self.work().set_up_to_date(false);
304 (|| -> BuildResult {
305 let actions = self.actions()?;
306
307 for action in actions {
308 let result: BuildResult = action.execute(self, project);
309 match result {
310 Ok(()) => {}
311 Err(e) => match e.kind() {
312 BuildException::StopAction => continue,
313 BuildException::StopTask => return Ok(()),
314 BuildException::Error(_) => return Err(e),
315 },
316 }
317 }
318
319 Ok(())
320 })()
321 } else {
322 self.work().set_up_to_date(true);
323 self.work().set_did_work(false);
324 debug!("skipping {} because it's up-to-date", self.task_id);
325
326 if let Some(output) = self.work.try_get_prev_output().cloned() {
327 debug!("Attempting to recover outputs from previous run");
328 self.task.recover_outputs(&output)?;
329 debug!("After task recovered: {:#x?}", self.task);
330 }
331
332 Ok(())
333 };
334
335 if self.work.get_input()?.any_inputs() {
336 if work.is_ok() {
337 if let Err(e) = self.work.store_execution_history() {
338 error!("encountered error while caching input: {}", e);
339 }
340 } else if let Err(e) = self.work.remove_execution_history() {
341 error!("encountered error while removing cached input: {}", e);
342 }
343 }
344
345 work
346 }
347
348 fn did_work(&self) -> bool {
349 self.work.did_work()
350 }
351
352 fn task_up_to_date(&self) -> bool {
353 *self.work.up_to_date()
354 }
355
356 fn group(&self) -> String {
357 self.group.clone()
358 }
359
360 fn description(&self) -> String {
361 self.description.clone()
362 }
363}