1
%% -*- erlang-indent-level: 2 -*-
2
%%-------------------------------------------------------------------
3
%% ``The contents of this file are subject to the Erlang Public License,
4
%% Version 1.1, (the "License"); you may not use this file except in
5
%% compliance with the License. You should have received a copy of the
6
%% Erlang Public License along with this software. If not, it can be
7
%% retrieved via the world wide web at http://www.erlang.org/.
9
%% Software distributed under the License is distributed on an "AS IS"
10
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11
%% the License for the specific language governing rights and limitations
14
%% Copyright 2006, Tobias Lindahl and Kostis Sagonas
19
%%%-------------------------------------------------------------------
20
%%% File : dialyzer_cl.erl
21
%%% Authors : Tobias Lindahl <tobiasl@csd.uu.se>
22
%%% Kostis Sagonas <kostis@it.uu.se>
23
%%% Description : The command line interface for the Dialyzer tool.
25
%%% Created : 27 Apr 2004 by Tobias Lindahl <tobiasl@csd.uu.se>
26
%%%-------------------------------------------------------------------
29
-export([start/1, check_init_plt/1]).
31
-include("dialyzer.hrl"). %% file is automatically generated
32
-include("hipe_icode_type.hrl").
34
-record(cl_state, {backend_pid,
40
nof_warnings=0::integer(),
41
return_status=0::integer()
44
start(Options) when is_list(Options) ->
45
start(dialyzer_options:build(Options));
46
start(#options{} = DialyzerOptions) ->
47
process_flag(trap_exit, true),
48
State = new_state(DialyzerOptions#options.init_plt),
49
NewState1 = init_output(State, DialyzerOptions),
51
NewState1#cl_state{legal_warnings=DialyzerOptions#options.legal_warnings,
52
output_plt=DialyzerOptions#options.output_plt},
53
InitAnalysis = build_analysis_record(NewState2, DialyzerOptions),
54
NewState3 = run_analysis(NewState2, InitAnalysis),
57
check_init_plt(Opts) ->
58
process_flag(trap_exit, true),
59
Quiet = get(dialyzer_options_quiet),
60
case dialyzer_plt:check_init_plt(Opts#options.plt_libs,
61
Opts#options.init_plt) of
62
{fail, MD5, Libs, InitPlt} ->
64
true -> io:format(" no\n", [])
66
case check_if_installed() of
68
Msg = " The initial PLT is not up-to-date.\n"
69
" Since Dialyzer is installed no new PLT will be built.\n"
70
" Please refer to the manual.\n",
71
io:format("~s", [Msg]),
74
Msg = " Creating initial PLT"
75
" (will take several minutes; please be patient)\n",
76
io:format("~s", [Msg]),
77
case create_init_plt(MD5, Libs, InitPlt, Opts#options.include_dirs) of
78
?RET_INTERNAL_ERROR -> error;
79
?RET_NOTHING_SUSPICIOUS -> ok;
80
?RET_DISCREPANCIES_FOUND -> ok
85
true -> io:format(" yes\n")
89
io:format(" no\n~s\n", [Msg]),
93
check_if_installed() ->
94
case filename:basename(code:lib_dir(dialyzer)) of
96
"dialyzer-" ++ _Version -> true
99
create_init_plt(MD5, Libs, InitPlt, IncludeDirs) ->
100
State = new_state_no_init(),
101
State1 = State#cl_state{output_plt=InitPlt},
102
Files = [filename:join(code:lib_dir(Lib), "ebin")|| Lib <- Libs],
103
Analysis = #analysis{fixpoint=first,
106
init_plt=dialyzer_plt:new(dialyzer_empty_plt),
107
include_dirs=IncludeDirs,
108
plt_info={MD5, Libs},
109
start_from=byte_code,
110
core_transform=succ_typings,
111
user_plt=State1#cl_state.user_plt,
112
supress_inline=true},
113
cl_loop(run_analysis(State1, Analysis)).
115
new_state(InitPlt) ->
116
NewInitPlt = dialyzer_plt:from_file(dialyzer_init_plt, InitPlt),
117
new_state1(NewInitPlt).
119
new_state_no_init() ->
122
new_state1(InitPlt) ->
123
UserPLT = dialyzer_plt:new(dialyzer_user_plt),
124
#cl_state{user_plt=UserPLT, init_plt=InitPlt}.
126
init_output(State, DialyzerOptions) ->
127
case DialyzerOptions#options.output_file of
131
case file:open(OutputFile, [write]) of
133
State#cl_state{output=File};
135
io:format("Could not open output file ~p, Reason ~p\n",
136
[OutputFile, Reason]),
141
maybe_close_output_file(State) ->
142
case State#cl_state.output of
144
File -> file:close(File)
147
%% ----------------------------------------------------------------
153
BackendPid = State#cl_state.backend_pid,
154
Output = State#cl_state.output,
156
{BackendPid, log, _LogMsg} ->
157
%io:format(Output,"Log: ~s", [_LogMsg]),
159
{BackendPid, warnings, Warnings} ->
160
NewState = print_warnings(State, Warnings),
162
{BackendPid, error, Msg} ->
163
io:format(Output, "~s", [Msg]),
164
cl_loop(State#cl_state{return_status=-1});
165
{BackendPid, done} ->
167
{BackendPid, ext_calls, ExtCalls} ->
168
Quiet = get(dialyzer_options_quiet),
170
true -> io:format("\nUnknown functions: ~p\n", [ExtCalls])
173
{'EXIT', BackendPid, {error, Reason}} ->
174
Msg = failed_anal_msg(Reason),
176
{'EXIT', BackendPid, Reason} when Reason =/= 'normal' ->
177
Msg = failed_anal_msg(Reason),
178
maybe_close_output_file(State),
181
%% io:format(Output, "Received ~p\n", [Other]),
185
failed_anal_msg(Reason) ->
186
io_lib:format("Analysis failed with error report: ~p\n", [Reason]).
188
print_warnings(State = #cl_state{output=Output}, Warnings) ->
189
NofOldWarnings = State#cl_state.nof_warnings,
190
case NofOldWarnings of
192
io:format(Output, "\n", []); %% warnings are just starting to appaer
196
io:format(Output, "~s", [Warnings]),
197
NofNewWarning = length(string:tokens(lists:flatten(Warnings), "\n")),
198
State#cl_state{nof_warnings=NofOldWarnings+NofNewWarning}.
201
io:format(State#cl_state.output, "~s", [Msg]),
202
return_value(State#cl_state{return_status=-1}).
204
return_value(State = #cl_state{return_status=-1}) ->
205
maybe_close_output_file(State),
207
return_value(State = #cl_state{nof_warnings=NofWarnings, output_plt=OutputPlt,
208
user_plt=UserPlt, init_plt=InitPlt}) ->
209
if OutputPlt =/= undefined ->
210
case dialyzer_plt:merge_and_write_file([InitPlt, UserPlt], OutputPlt) of
213
error(State, io_lib:format("Error while writing plt to file: ~w\n",
219
maybe_close_output_file(State),
220
if NofWarnings =:= 0 ->
221
?RET_NOTHING_SUSPICIOUS;
223
?RET_DISCREPANCIES_FOUND
227
%% ----------------------------------------------------------------
232
build_analysis_record(State, DialyzerOptions) ->
233
PLT = State#cl_state.user_plt,
234
From = DialyzerOptions#options.from,
235
IncludeDirs = DialyzerOptions#options.include_dirs,
236
Defines = DialyzerOptions#options.defines,
237
SupressInline = DialyzerOptions#options.supress_inline,
238
Files0 = ordsets:from_list(DialyzerOptions#options.files),
239
Files1 = ordsets:from_list(lists:concat([filelib:wildcard(F) || F <- Files0])),
240
Files2 = add_files_rec(DialyzerOptions#options.files_rec, From),
241
Files = ordsets:union(Files1, Files2),
242
CoreTransform = DialyzerOptions#options.core_transform,
243
InitPlt = State#cl_state.init_plt,
244
#analysis{fixpoint=first, core_transform=CoreTransform,
245
defines=Defines, granularity=all,
246
include_dirs=IncludeDirs, init_plt=InitPlt, user_plt=PLT,
247
files=Files, start_from=From, supress_inline=SupressInline}.
250
add_files_rec(Files, From) ->
251
Files1 = ordsets:from_list(Files),
252
Dirs1 = ordsets:filter(fun(X) -> filelib:is_dir(X) end, Files1),
253
Dirs2 = ordsets:union(Dirs1, all_subdirs(Dirs1)),
254
FinalFiles = ordsets:union(Files1, Dirs2),
256
byte_code -> filter_files(FinalFiles, ".beam");
257
src_code -> filter_files(FinalFiles, ".erl")
261
all_subdirs(Dirs, []).
263
all_subdirs([Dir|T], Acc) ->
264
{ok, Files} = file:list_dir(Dir),
265
SubDirs = lists:zf(fun(F) ->
266
SubDir = filename:join(Dir, F),
267
case filelib:is_dir(SubDir) of
268
true -> {true, SubDir};
272
NewAcc = ordsets:union(ordsets:from_list(SubDirs), Acc),
273
all_subdirs(T++SubDirs, NewAcc);
274
all_subdirs([], Acc) ->
277
filter_files(Files, Extension) ->
279
filename:extension(X) =:= Extension
281
(filelib:is_dir(X) andalso contains_files(X, Extension))
283
lists:filter(Fun, Files).
285
contains_files(Dir, Extension) ->
286
{ok, Files} = file:list_dir(Dir),
287
lists:any(fun(X) -> filename:extension(X) =:= Extension end, Files).
290
run_analysis(State, Analysis) ->
292
LegalWarnings = State#cl_state.legal_warnings,
294
dialyzer_analysis_callgraph:start(Self, LegalWarnings, Analysis)
296
BackendPid = spawn_link(Fun),
297
State#cl_state{backend_pid=BackendPid}.