Merge remote-tracking branch 'canonical/next'
[sinan.git] / src / sin_task_dialyzer.erl
blob50a57bda2152f49f43d7db32727bcd9efee8a9ea
1 %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
2 %%%---------------------------------------------------------------------------
3 %%% @author Eric Merritt
4 %%% @doc
5 %%% Uses dialyzer to analyze the project sources and output messages.
6 %%% @end
7 %%% @copyright (C) 2006-2011 Erlware
8 %%%---------------------------------------------------------------------------
9 -module(sin_task_dialyzer).
11 -behaviour(sin_task).
13 -include_lib("sinan/include/sinan.hrl").
15 %% API
16 -export([description/0, do_task/2,
17 format_exception/1]).
19 -define(TASK, dialyzer).
20 -define(DEPS, [build]).
23 %%====================================================================
24 %% API
25 %%====================================================================
26 %% @doc provide a description of the system for the caller
27 -spec description() -> sin_task:task_description().
28 description() ->
30 Desc = "
31 dialyzer Task
32 =============
34 This task runs dialyzer on a project. It checks the 'dialyzer' state of the
35 project and builds or updates the relevant per project plt files. This task may
36 take a (very) long time to complete.
38 One problem that dialyzer has is that all applications being analyzed project
39 apps and dependencies must be built with debug information. If not, dialyzer
40 will blow up with some not very useful information. You can get around this by
41 asking the dialyzer task to ignore certain applications.
43 Do this in your build config with the following entry.
45 {dialyzer_ignore, [IgnoredApps::atom()]}.
47 The downside, of course, is figuring out which apps actually have the
48 problem. This task tries to help by printing out a list of applications that
49 where under analysis when the blowup occurred.
51 By default all dialyzer warnings are enabled. You can
52 override this with the directive
54 {dialyzer_warnings, [Warnings::term()]}.
56 Just put the warning options listed in the dialyzer
57 documentation to get the specific warnings that you would like
58 to see. Unfortunately, you can not do over ride that on a per
59 application basis as dialyzer is a whole program analysis
60 system. ",
62 #task{name = ?TASK,
63 task_impl = ?MODULE,
64 bare = false,
65 example = "dialyzer",
66 short_desc = "Run the Dialyzer analyzer on the project",
67 deps = ?DEPS,
68 desc = Desc,
69 opts = []}.
71 do_task(Config0, State0) ->
72 sin_log:verbose(Config0, "Starting analyzation, this may take awhile ..."),
73 BuildDir = sin_state:get_value(build_dir,State0),
74 {ProjectPlt, DepPlt} = get_plt_location(BuildDir),
75 DepList = sin_state:get_value(release_deps, State0),
76 AppList = sin_state:get_value(project_apps, State0),
77 sin_log:verbose(Config0, "Doing plt for all dependencies ..."),
78 update_dep_plt(Config0, State0, DepPlt, DepList),
79 sin_log:verbose(Config0, "Doing plt for project apps ..."),
80 update_dep_plt(Config0, State0, ProjectPlt, AppList),
82 WarningTypes = Config0:match(dialyzer_warnings, default_warnings()),
83 Paths = [filename:join(Path, "ebin") ||
84 #app{path=Path} <- AppList],
85 Opts = [{analysis_type, succ_typings},
86 {from, byte_code},
87 {files_rec, Paths},
88 {warnings, WarningTypes},
89 {plts, [ProjectPlt, DepPlt]}],
90 try
91 case dialyzer:run(Opts) of
92 [] ->
93 State0;
94 Warnings ->
95 ?SIN_RAISE(State0,
96 {warnings, Warnings, AppList})
97 end
98 catch
99 _:{dialyzer_error, Error} ->
100 ?SIN_RAISE(State0,
101 {error_processing_apps, Error, AppList})
102 end,
103 State0.
105 -spec format_exception(sin_exceptions:exception()) ->
106 string().
107 format_exception(?SIN_EXEP_UNPARSE(_,
108 {error_processing_apps, Error, AppList})) ->
109 [io_lib:format("~s~n", [Error]),
110 "while processing the following apps~n",
111 lists:map(fun(#app{name=Name}) ->
112 io_lib:format(" ~s~n", [Name])
113 end, AppList)];
114 format_exception(?SIN_EXEP_UNPARSE(_,
115 {warnings, Warnings, _AppList})) ->
116 [lists:map(fun(Warning) ->
117 dialyzer:format_warning(Warning)
118 end, Warnings), "~n"];
119 format_exception(Exception) ->
120 sin_exceptions:format_exception(Exception).
122 %%====================================================================
123 %%% Internal functions
124 %%====================================================================
125 get_plt_location(BuildDir) ->
126 {filename:join([BuildDir, "project.plt"]),
127 filename:join([BuildDir, "deps.plt"])}.
129 update_dep_plt(Config0, State0, DepPlt, AppList0) ->
130 Ignores = Config0:match(dialyzer_ignore, []),
131 AppList1 = [App ||
132 App = #app{name=Name} <- AppList0,
133 not lists:member(Name, Ignores)],
134 Opts0 =
135 case sin_utils:file_exists(State0, DepPlt) of
136 true ->
137 sin_log:verbose(Config0, "Plt is built, checking/updating ..."),
138 [{analysis_type, plt_check},
139 {plts, [DepPlt]}];
140 false ->
141 sin_log:verbose(Config0, "Building the plt, this is really going to "
142 "take a long time ..."),
143 [{analysis_type, plt_build},
144 {output_plt, DepPlt}]
145 end,
147 Paths = [filename:join(Path, "ebin") ||
148 #app{path=Path} <- AppList1],
150 Opts = [{files_rec, Paths},
151 {from, byte_code}] ++ Opts0,
153 dialyzer:run(Opts)
154 catch
155 _:{dialyzer_error, Error} ->
156 ?SIN_RAISE(State0,
157 {error_processing_apps, Error, AppList1})
158 end.
160 default_warnings() ->
161 [no_return,
162 no_unused,
163 no_improper_lists,
164 no_fun_app,
165 no_match,
166 no_opaque,
167 no_fail_call,
168 error_handling,
169 race_conditions,
170 unmatched_returns,
171 underspecs].