assemble_core/task/
executable.rs

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
28/// The wrapped task itself
29pub 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    /// Initialize the executable.
74    pub fn initialize(&mut self, project: &Project) -> ProjectResult {
75        T::initialize(self, project)
76    }
77
78    /// Configures the io of this executable. Ran after the task is initialized.
79    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    /// Check to see if this task is already up-to-date before execution begins. Up-to-date handlers
177    /// are ran first. If all up-to-date handlers return true, then shortcuts to returning true. If none declared, this task is always
178    /// not up-to-date.
179    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                // first run custom up-to-date checks
190                if !self.handler_up_to_date() {
191                    return Ok(false);
192                }
193
194                // Check if input has changed
195                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                // Check if output is not up to date
202                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    /// Add an up-to-date check
213    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
280/// If set to true, all tasks will always run
281pub 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}