erlang的xref是个好东西,为了方便项目中使用,简单的进行了一个封装
%%%===================模块描述(开始)=====================
%% 代码引用检查
%% 1.检查的路径是基于EmakeFile的
%% 2.每个需要检查的路径必须是debug编译的,否则检查失败
%% 3.如果正常结束,不需要做任何操作,如果异常退出了,下次开始检查前需要调用stop方法
%%%===================模块描述(结束)=====================
-module(z_test_xref).
%%%=======================STATEMENT====================
-copyright('youkia,www.youkia.net').
-author('lb,lb@youkia.net').
-time("2020/4/3 16:15").
%%%=======================EXPORT=======================
-export([start/0, start/3, stop/0]).
%%%=======================INCLUDE======================
%%%=======================RECORD=======================
%%%=======================DEFINE=======================
-define(EXCLUDE, ["lib"]).%排除项目
-define(TYPE, [undefined_function_calls, undefined_functions, locals_not_used, exports_not_used, deprecated_function_calls, deprecated_functions]).%检查项目
%%%=================EXPORTED FUNCTIONS=================
start() ->
start("./Emakefile", "./", ?EXCLUDE).
%% ----------------------------------------------------
%% 启动
%% 需要指定emakefile的路径
%% ----------------------------------------------------
start(Emakefile, FilePath, Exclude) ->
case file:consult(Emakefile) of
{ok, List} ->
case xref:start(?MODULE) of
{ok, _} ->
xref:set_default(?MODULE, [{verbose, true}, {warnings, true}]),
xref:add_release(?MODULE, code:lib_dir(), {name, erlang_lib}),
Paths = [z_lib:get_value(V, 'outdir', none) || {_, V} <- List],
case load_path(Paths, []) of
{ok, Mods} ->
case check(FilePath, Exclude, ?TYPE, Mods) of
ok ->
stop();
Error ->
Error
end;
Error ->
Error
end;
Error ->
z_log:warn(?MODULE, ?FUNCTION_NAME, "start_xref_error", [Error]),
start_xref_error
end;
Error ->
z_log:warn(?MODULE, ?FUNCTION_NAME, "emake_file_error", [Emakefile, Error]),
emake_file_error
end.
%% ----------------------------------------------------
%% 停止
%% ----------------------------------------------------
stop() ->
xref:stop(?MODULE).
%%%===================LOCAL FUNCTIONS==================
%% ----------------------------------------------------
%% 加载beam路径
%% ----------------------------------------------------
load_path([], Mods) ->
{ok, Mods};
load_path([Path | Paths], Mods) ->
case xref:add_directory(?MODULE, [Path]) of
{ok, _} ->
{ok, Files} = file:list_dir(Path),
Project = get_project(Path),
load_path(Paths, [{list_to_atom(filename:rootname(Str)), Project} || Str <- Files] ++ Mods);
Error ->
z_log:warn(?MODULE, ?FUNCTION_NAME, "load_path_error", [Path, Error]),
load_path_error
end.
check(_FilePath, _Exclude, [], _Mods) ->
ok;
check(FilePath, Exclude, [Type | Types], Mods) ->
case xref:analyze(?MODULE, Type) of
{ok, List} ->
{Name, Head} = get_title(Type),
write(Exclude, FilePath ++ Name, Head, List, Mods),
check(FilePath, Exclude, Types, Mods);
Error ->
z_log:warn(?MODULE, ?FUNCTION_NAME, "undefined_function_calls_error", [Error]),
check_error
end.
%%获取文件名字以及行首
get_title(undefined_function_calls) ->
{"xref_call_undefined_function.csv", "call function,undefined function"};
get_title(undefined_functions) ->
{"xref_undefined_function.csv", "undefined function"};
get_title(locals_not_used) ->
{"xref_unused_function.csv", "unused function"};
get_title(exports_not_used) ->
{"xref_export_unused_function.csv", "unexport function"};
get_title(deprecated_function_calls) ->
{"xref_call_deprecated_function.csv", "call function,deprecated function"};
get_title(deprecated_functions) ->
{"xref_deprecated_function.csv", "deprecated function"}.
write(_Exclude, Name, _, [], _Mods) ->
[file:close(Name) || not is_list(Name)];
write(Exclude, Name, Head, List, Mods) when is_list(Name) ->
filelib:ensure_dir(Name),
{ok, IO} = file:open(Name, [write]),
file:write(IO, Head),
file:write(IO, "\r\n"),
write(Exclude, IO, Head, List, Mods);
write(Exclude, IO, Head, [{{M1, F1, A1}, {M2, F2, A2}} | List], Mods) ->
case lists:keyfind(M1, 1, Mods) of
false ->
ok;
{_, Project} ->
case lists:member(Project, Exclude) of
true ->
ok;
_ ->
file:write(IO, lists:concat([M1, ":", F1, "/", A1, ",", M2, ":", F2, "/", A2, "\r\n"]))
end
end,
write(Exclude, IO, Head, List, Mods);
write(Exclude, IO, Head, [{M1, F1, A1} | List], Mods) ->
case lists:keyfind(M1, 1, Mods) of
false ->
ok;
{_, Project} ->
case lists:member(Project, Exclude) of
true ->
ok;
_ ->
file:write(IO, lists:concat([M1, ":", F1, "/", A1, "\r\n"]))
end
end,
write(Exclude, IO, Head, List, Mods).
get_project(Path) ->
get_project_(lists:reverse(Path)).
get_project_([$/, $n, $i, $b, $e, $. | Rest]) ->
lists:last(string:tokens(lists:reverse(Rest), "/"));
get_project_([$n, $i, $b, $e, $. | Rest]) ->
lists:last(string:tokens(lists:reverse(Rest), "/"));
get_project_([_ | Rest]) ->
get_project_(Rest).