Merge remote-tracking branch 'canonical/next'
[sinan.git] / src / sin_task.erl
blob2ac3fa9375fd3211d59ab15c6316f405321c7947
1 %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
2 %%%-------------------------------------------------------------------
3 %%% @author Eric Merritt <ericbmerritt@gmail.com>
4 %%% @copyright (C) 2006 - 2011, Eric Merritt
5 %%% @doc
6 %%% Provides task sorting and manipulation support to the sinan.
7 %%% @end
8 %%%-------------------------------------------------------------------
9 -module(sin_task).
12 -include_lib("sinan/include/sinan.hrl").
14 -export([get_task/2,
15 get_task_list/2,
16 get_tasks/0,
17 behaviour_info/1,
18 format_exception/1,
19 ensure_started/1]).
21 -export_type([task_description/0,
22 task_name/0]).
24 %%====================================================================
25 %%% Types
26 %%====================================================================
28 -type task_description() :: record(task).
29 -type task_name() :: atom().
31 %%====================================================================
32 %%% API Functions
33 %%====================================================================
34 -spec ensure_started(atom()) -> ok.
35 ensure_started(App) ->
36 case application:start(App) of
37 {error, {already_started, _}} ->
38 ok;
39 ok ->
40 ok;
41 Error ->
42 erlang:throw({error_starting_app, App, Error})
43 end.
45 %% @doc get a specific task description
46 -spec get_task(sin_state:state(), task_name()) -> [task_name()].
47 get_task(State, TaskName) ->
48 Tasks = get_tasks(),
49 get_task(State, TaskName, Tasks).
51 %% @doc get a dependency ordered list of tasks from the system.
52 -spec get_task_list(sin_state:state(), task_name()) -> [task_name()].
53 get_task_list(State, TaskName) ->
54 Tasks = get_tasks(),
55 RootTask = get_task(State, TaskName, Tasks),
56 lists:map(fun(DepTaskName) ->
57 get_task(State, DepTaskName, Tasks)
58 end,
59 process_deps(State, RootTask, Tasks)).
61 %% @doc get a list of all tasks in the system
62 -spec get_tasks() -> [record(task)].
63 get_tasks() ->
64 [sin_task_depends:description(),
65 sin_task_dialyzer:description(),
66 sin_task_version:description(),
67 sin_task_eunit:description(),
68 sin_task_proper:description(),
69 sin_task_eqc:description(),
70 sin_task_shell:description(),
71 sin_task_release:description(),
72 sin_task_help:description(),
73 sin_task_gen:description(),
74 sin_task_doc:description(),
75 sin_task_dist:description(),
76 sin_task_clean:description(),
77 sin_task_build:description(),
78 sin_task_xref:description(),
79 sin_task_erts:description(),
80 sin_task_escript:description(),
81 sin_task_echo:description(),
82 sin_task_cucumber:description()].
84 %% @doc define the behaviour for tasks.
85 behaviour_info(callbacks) ->
86 [{description, 0}, {do_task, 2}];
87 behaviour_info(_) ->
88 undefined.
90 %% @doc Format an exception thrown by this module
91 -spec format_exception(sin_exceptions:exception()) ->
92 string().
93 format_exception(Exception) ->
94 sin_exceptions:format_exception(Exception).
96 %%====================================================================
97 %%% Internal functions
98 %%====================================================================
100 -spec get_task(sin_state:state(),
101 task_name(), [task_description()]) -> task_description().
102 get_task(_State, TaskName, [Task = #task{name = TaskName} | _]) ->
103 Task;
104 get_task(State, TaskName, [_ | Rest]) ->
105 get_task(State, TaskName, Rest);
106 get_task(State, TaskName, _) ->
107 ?SIN_RAISE(State, {task_not_found, TaskName}).
109 process_deps(State, Task, Tasks) ->
110 {DepChain, _, _} = process_deps(State, Task, Tasks, []),
111 ['NONE' | Rest] =
112 reorder_tasks(State,
113 lists:flatten([{'NONE', Task#task.name} | DepChain])),
114 Rest.
116 process_deps(State, Task, Tasks, Seen) ->
117 case lists:member(Task, Seen) of
118 true ->
119 {[], Tasks, Seen};
120 false ->
121 Deps = Task#task.deps,
122 DepList = lists:map(fun(Dep) ->
123 {Dep, Task#task.name}
124 end, Deps),
125 {NewDeps, _, NewSeen} =
126 lists:foldl(fun(Arg, Acc) ->
127 process_dep(State, Arg, Acc)
128 end,
129 {[], Tasks, Seen}, Deps),
130 {[DepList | NewDeps], Tasks, NewSeen}
131 end.
133 process_dep(State, TaskName, {Deps, Tasks, Seen}) ->
134 Task = get_task(State, TaskName, Tasks),
135 {NewDeps, _, NewSeen} = process_deps(State,
136 Task, Tasks, [TaskName | Seen]),
137 {[Deps | NewDeps], Tasks, NewSeen}.
139 %% @doc Reorder the tasks according to thier dependency set.
140 reorder_tasks(State, OTaskList) ->
141 case sin_topo:sort(OTaskList) of
142 {ok, TaskList} ->
143 TaskList;
144 {cycle, _} ->
145 ?SIN_RAISE(State, cycle_fault,
146 "There was a cycle in the task list. "
147 "Unable to complete build!")
148 end.
150 %%====================================================================
151 %%% Tests
152 %%====================================================================