diff options
Diffstat (limited to 'lib/reltool/src/reltool_utils.erl')
-rw-r--r-- | lib/reltool/src/reltool_utils.erl | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl new file mode 100644 index 0000000000..8d52ade9be --- /dev/null +++ b/lib/reltool/src/reltool_utils.erl @@ -0,0 +1,555 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(reltool_utils). + +%% Public +-compile([export_all]). + +-include_lib("kernel/include/file.hrl"). +-include_lib("wx/include/wx.hrl"). +-include("reltool.hrl"). + +root_dir() -> + code:root_dir(). + +erl_libs() -> + case os:getenv("ERL_LIBS") of + false -> + []; + LibStr -> + string:tokens(LibStr, ":;") + end. + +lib_dirs(Dir) -> + case erl_prim_loader:list_dir(Dir) of + {ok, Files} -> + [F || F <- Files, + filelib:is_dir(filename:join([Dir, F]), + erl_prim_loader)]; + error -> + [] + end. + +%% "asn1-1.6.2" -> {"asn1", "1.6.2"}; "asn1" -> {"asn1", ""} +split_app_name(Name) -> + Pred = + fun(Elem) -> + if + Elem =:= $\. -> true; + Elem >= $0, Elem =< $9 -> true; + true -> false + end + end, + case lists:splitwith(Pred, lists:reverse(Name)) of + {Vsn, [$- | App]} -> + {list_to_atom(lists:reverse(App)), lists:reverse(Vsn)}; + _ -> + {list_to_atom(Name), ""} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +prim_consult(Bin) when is_binary(Bin) -> + case erl_scan:string(binary_to_list(Bin)) of + {ok, Tokens, _EndLine} -> + prim_parse(Tokens, []); + {error, {_ErrorLine, Module, Reason}, _EndLine} -> + {error, Module:format_error(Reason)} + end; +prim_consult(FullName) when is_list(FullName) -> + case erl_prim_loader:get_file(FullName) of + {ok, Bin, _} -> + prim_consult(Bin); + error -> + {error, file:format_error(enoent)} + end. + +prim_parse(Tokens, Acc) -> + case lists:splitwith(fun(T) -> element(1,T) =/= dot end, Tokens) of + {[], []} -> + {ok, lists:reverse(Acc)}; + {Tokens2, [{dot,_} = Dot | Rest]} -> + case erl_parse:parse_term(Tokens2 ++ [Dot]) of + {ok, Term} -> + prim_parse(Rest, [Term | Acc]); + {error, {_ErrorLine, Module, Reason}} -> + {error, Module:format_error(Reason)} + end; + {Tokens2, []} -> + case erl_parse:parse_term(Tokens2) of + {ok, Term} -> + {ok, lists:reverse([Term | Acc])}; + {error, {_ErrorLine, Module, Reason}} -> + {error, Module:format_error(Reason)} + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +default_rels() -> + Kernel = #rel_app{name = kernel, incl_apps = []}, + Stdlib = #rel_app{name = stdlib, incl_apps = []}, + Sasl = #rel_app{name = sasl, incl_apps = []}, + [ + #rel{name = ?DEFAULT_REL_NAME, + vsn = "1.0", + rel_apps = [Kernel, Stdlib]}, + #rel{name = "start_sasl", + vsn = "1.0", + rel_apps = [Kernel, Sasl, Stdlib]} + ]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +assign_image_list(ListCtrl) -> + Art = wxImageList:new(16,16), + [wxImageList:add(Art, wxArtProvider:getBitmap(Image, [{size, {16,16}}])) + || Image <- ["wxART_ERROR", + "wxART_WARNING", + "wxART_QUESTION", + "wxART_TICK_MARK", + "wxART_CROSS_MARK", + "wxART_GO_HOME"]], + wxListCtrl:assignImageList(ListCtrl, Art, ?wxIMAGE_LIST_SMALL). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_latest_resize(#wx{obj = ObjRef, event = #wxSize{}} = Wx) -> + receive + #wx{obj = ObjRef, event = #wxSize{}} = Wx2 -> + get_latest_resize(Wx2) + after 10 -> + Wx + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +mod_conds() -> + ["all (ebin + app file)", "ebin + derived", "app file + derived", "derived", "none"]. + +list_to_mod_cond(List) -> + case List of + "all" ++ _ -> all; + "ebin" ++ _ -> ebin; + "app" ++ _ -> app; + "derived" -> derived; + "none" -> none + end. + +mod_cond_to_index(ModCond) -> + case ModCond of + all -> 0; + ebin -> 1; + app -> 2; + derived -> 3; + undefined -> 3; + none -> 4 + end. + +incl_conds() -> + ["include", "exclude", "derived"]. + +list_to_incl_cond(List) -> + case List of + "include" -> include; + "exclude" -> exclude; + "derived" -> derived + end. + +incl_cond_to_index(ModCond) -> + case ModCond of + include -> 0; + exclude -> 1; + derived -> 2 + end. + +elem_to_index(Elem, List) -> + elem_to_index(Elem, List, 1). + +elem_to_index(Elem, [H | T], Index) -> + case Elem =:= H of + true -> Index; + false -> elem_to_index(Elem, T, Index + 1) + end; +elem_to_index(Elem, [], _) -> + erlang:error({not_found, Elem}). + +app_dir_test(Dir1, Dir2) -> + {Name1, Vsn1, Parent1} = split_app_dir(Dir1), + {Name2, Vsn2, Parent2} = split_app_dir(Dir2), + if + Name1 < Name2 -> true; + Name1 > Name2 -> false; + Vsn1 < Vsn2 -> false; + Vsn1 > Vsn2 -> true; + Parent1 < Parent2 -> true; + true -> false + end. + +split_app_dir(Dir) -> + ParentDir = filename:dirname(Dir), + Base = filename:basename(Dir), + {Name, Vsn} = split_app_name(Base), + Vsn2 = + try + [list_to_integer(N) || N <- string:tokens(Vsn, ".")] + catch + _:_ -> + Vsn + end, + {Name, Vsn2, ParentDir}. + +get_item(ListCtrl) -> + case wxListCtrl:getItemCount(ListCtrl) of + 0 -> + undefined; + _ -> + case wxListCtrl:getNextItem(ListCtrl, + -1, + [{geometry, ?wxLIST_NEXT_ALL}, + {state, ?wxLIST_STATE_SELECTED}]) of + -1 -> + ItemNo = wxListCtrl:getTopItem(ListCtrl), + case wxListCtrl:getItemText(ListCtrl, ItemNo) of + "" -> + undefined; + Text -> + {ItemNo, Text} + end; + ItemNo -> + Text = wxListCtrl:getItemText(ListCtrl, ItemNo), + {ItemNo, Text} + end + end. + +get_items(ListCtrl) -> + case wxListCtrl:getItemCount(ListCtrl) of + 0 -> + []; + Count -> + case get_selected_items(ListCtrl, -1, []) of + [] -> + ItemNo = wxListCtrl:getTopItem(ListCtrl), + case wxListCtrl:getItemText(ListCtrl, ItemNo) of + "" -> + []; + Text when Text =/= ?MISSING_APP_TEXT -> + [{ItemNo, Text}]; + _MissingText when Count > 1 -> + case wxListCtrl:getItemText(ListCtrl, ItemNo + 1) of + "" -> + []; + Text -> + [{ItemNo, Text}] + end; + _MissingText -> + [] + end; + Items -> + Items + end + end. + +get_selected_items(ListCtrl, PrevItem, Acc) -> + case wxListCtrl:getNextItem(ListCtrl, + PrevItem, + [{geometry, ?wxLIST_NEXT_ALL}, + {state, ?wxLIST_STATE_SELECTED}]) of + -1 -> + Acc; + ItemNo -> + case wxListCtrl:getItemText(ListCtrl, ItemNo) of + Text when Text =/= ?MISSING_APP_TEXT -> + get_selected_items(ListCtrl, ItemNo, [{ItemNo, Text} | Acc]); + _Text -> + get_selected_items(ListCtrl, ItemNo, Acc) + end + end. + +select_items(_ListCtrl, _OldItems, []) -> + %% No new items. Nothing to select. + false; +select_items(ListCtrl, [], Items) -> + %% No old selection. Select first. + select_item(ListCtrl, Items); +select_items(ListCtrl, _OldItems, [Item]) -> + %% Only one new item. Select it. + select_item(ListCtrl, [Item]); +select_items(ListCtrl, OldItems, NewItems) -> + %% Try to propagate old selection to new items. + Filter = + fun({_OldItemNo, Text}) -> + case lists:keysearch(Text, 2, NewItems) of + {value, Item} -> {true, Item}; + false -> false + end + end, + case lists:zf(Filter, OldItems) of + [] -> + %% None of the old selections are valid. Select the first. + select_item(ListCtrl, NewItems); + ValidItems -> + %% Some old selections are still valid. Select them again. + lists:foreach(fun(Item) -> select_item(ListCtrl, [Item]) end, ValidItems) + end. + +select_item(ListCtrl, [{ItemNo, Text} | Items]) -> + case Text =:= ?MISSING_APP_TEXT of + true -> + select_item(ListCtrl, Items); + false -> + StateMask = ?wxLIST_STATE_SELECTED, + State = wxListCtrl:getItemState(ListCtrl, ItemNo, StateMask), + State2 = State bor ?wxLIST_STATE_SELECTED, + wxListCtrl:setItemState(ListCtrl, ItemNo, State2, StateMask), + wxListCtrl:refreshItem(ListCtrl, ItemNo) + end; +select_item(_ListCtrl, []) -> + ok. + +safe_keysearch(Key, Pos, List, Mod, Line) -> + case lists:keysearch(Key, Pos, List) of + false -> + io:format("~p(~p): lists:keysearch(~p, ~p, ~p) -> false\n", + [Mod, Line, Key, Pos, List]), + erlang:error({Mod, Line, lists, keysearch, [Key, Pos, List]}); + {value, Val} -> + Val + end. + +print(X, X, Format, Args) -> + io:format(Format, Args); +print(_, _, _, _) -> + ok. + +%% -define(SAFE(M,F,A), safe(M, F, A, ?MODULE, ?LINE)). +%% +%% safe(M, F, A, Mod, Line) -> +%% case catch apply(M, F, A) of +%% {'EXIT', Reason} -> +%% io:format("~p(~p): ~p:~p~p -> ~p\n", [Mod, Line, M, F, A, Reason]), +%% timer:sleep(infinity); +%% Res -> +%% Res +%% end. + +return_first_error(Status, NewError) when is_list(NewError) -> + case Status of + {ok, _Warnings} -> + {error, NewError}; + {error, OldError} -> + {error, OldError} + end. + +add_warning(Status, Warning) -> + case Status of + {ok, Warnings} -> + {ok, [Warning | Warnings]}; + {error, Error} -> + {error, Error} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_dir(Dir) -> + filelib:ensure_dir(Dir), + case file:make_dir(Dir) of + ok -> + ok; + {error, eexist} -> + ok; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("create dir ~s: ~s\n", [Dir, Text]) + end. + +list_dir(Dir) -> + case erl_prim_loader:list_dir(Dir) of + {ok, Files} -> + Files; + error -> + Text = file:format_error(enoent), + throw_error("list dir ~s: ~s\n", [Dir, Text]) + end. + +read_file_info(File) -> + case file:read_file_info(File) of + {ok, Info} -> + Info; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("read file info ~s: ~s\n", [File, Text]) + end. + +write_file_info(File, Info) -> + case file:write_file_info(File, Info) of + ok -> + ok; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("write file info ~s: ~s\n", [File, Text]) + end. + +read_file(File) -> + case file:read_file(File) of + {ok, Bin} -> + Bin; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("read file ~s: ~s\n", [File, Text]) + end. + +write_file(File, IoList) -> + case file:write_file(File, IoList) of + ok -> + ok; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("write file ~s: ~s\n", [File, Text]) + end. + +recursive_delete(Dir) -> + case filelib:is_dir(Dir) of + true -> + case file:list_dir(Dir) of + {ok, Files} -> + Fun = fun(F) -> recursive_delete(filename:join([Dir, F])) end, + lists:foreach(Fun, Files), + delete(Dir, directory); + {error, enoent} -> + ok; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("delete file ~s: ~s\n", [Dir, Text]) + end; + false -> + delete(Dir, regular) + end. + +delete(File, Type) -> + case do_delete(File, Type) of + ok -> + ok; + {error, enoent} -> + ok; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("delete file ~s: ~s\n", [File, Text]) + end. + +do_delete(File, regular) -> + file:delete(File); +do_delete(Dir, directory) -> + file:del_dir(Dir). + +recursive_copy_file(From, To) -> + case erl_prim_loader:list_dir(From) of + {ok, Files} -> + %% Copy all files in the directory + create_dir(To), + Copy = + fun(F) -> + recursive_copy_file(filename:join([From, F]), + filename:join([To, F])) + end, + lists:foreach(Copy, Files); + error -> + %% Copy single file + copy_file(From, To) + end. + +copy_file(From, To) -> + case erl_prim_loader:get_file(From) of + {ok, Bin, _} -> + case file:write_file(To, Bin) of + ok -> + FromInfo = read_file_info(From), + ToInfo = read_file_info(To), + FromMode = FromInfo#file_info.mode, + ToMode = ToInfo#file_info.mode, + ToMode2 = FromMode bor ToMode, + FileInfo = FromInfo#file_info{mode = ToMode2}, + write_file_info(To, FileInfo), + ok; + {error, Reason} -> + Text = file:format_error(Reason), + throw_error("copy file ~s -> ~s: ~s\n", [From, To, Text]) + end; + error -> + Text = file:format_error(enoent), + throw_error("copy file ~s -> ~s: ~s\n", [From, To, Text]) + end. + +throw_error(Format, Args) -> + throw({error, lists:flatten(io_lib:format(Format, Args))}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +decode_regexps(Key, {add, Regexps}, Old) when is_list(Regexps) -> + do_decode_regexps(Key, Regexps, Old); +decode_regexps(_Key, {del, Regexps}, Old) when is_list(Regexps) -> + [Re || Re <- Old, not lists:member(Re#regexp.source, Regexps)]; +decode_regexps(Key, Regexps, _Old) when is_list(Regexps) -> + do_decode_regexps(Key, Regexps, []); +decode_regexps(Key, Regexps, _Old) when is_list(Regexps) -> + Text = lists:flatten(io_lib:format("~p", [{Key, Regexps}])), + throw({error, "Illegal option: " ++ Text}). + +do_decode_regexps(Key, [Regexp | Regexps], Acc) -> + case catch re:compile(Regexp, []) of + {ok, MP} -> + do_decode_regexps(Key, Regexps, [#regexp{source = Regexp, compiled = MP} | Acc]); + _ -> + Text = lists:flatten(io_lib:format("~p", [{Key, Regexp}])), + throw({error, "Illegal option: " ++ Text}) + end; +do_decode_regexps(_Key, [], Acc) -> + lists:sort(Acc). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +default_val(Val, Default) -> + case Val of + undefined -> Default; + _ -> Val + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +call(Name, Msg) when is_atom(Name) -> + call(whereis(Name), Msg); +call(Pid, Msg) when is_pid(Pid) -> + Ref = erlang:monitor(process, Pid), + Pid ! {call, self(), Ref, Msg}, + receive + {Ref, Reply} -> + Reply; + {'EXIT', Pid, Reason} -> + erlang:demonitor(Ref, [flush]), + {error, Reason}; + {'DOWN', Ref, _, _, Reason} -> + {error, Reason} + end. + +cast(Pid, Msg) -> + Pid ! {cast, self(), Msg}, + ok. + +reply(Pid, Ref, Msg) -> + Pid ! {Ref, Msg}. |