Erlang之xref

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).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值