Current section
Files
Jump to
Current section
Files
src/rebar3_appup_utils.erl
%% -------------------------------------------------------------------
%%
%% Copyright (c) 2016 Luis Rascão. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
-module(rebar3_appup_utils).
-export([prop_check/3,
make_proplist/2,
find_files/3,
find_files_by_ext/2, find_files_by_ext/3,
now_str/0,
get_sub_dirs/1,
appup_plugin_appinfo/1,
find_app_info/2,
load_module_from_beam/2,
unload_module_from_beam/2,
beam_rel_path/2, beam_rel_path/4,
get_abstract_code/2,
tmp_filename/0,
find_app_by_name/2]).
%% Helper function for checking values and aborting when needed
%% @spec prop_check(boolean(),_,_) -> any().
prop_check(true, _, _) -> true;
prop_check(false, Msg, Args) -> rebar_api:abort(Msg, Args).
%% @spec make_proplist([tuple()],_) -> any().
make_proplist([{_,_}=H|T], Acc) ->
make_proplist(T, [H|Acc]);
make_proplist([H|T], Acc) ->
App = element(1, H),
Ver = element(2, H),
make_proplist(T, [{App,Ver}|Acc]);
make_proplist([], Acc) ->
Acc.
%% @spec find_files(atom() | [atom() | [any()] | char()],string(),boolean()) -> any().
find_files(Dir, Regex, Recursive) ->
filelib:fold_files(Dir, Regex, Recursive,
fun(F, Acc) -> [F | Acc] end, []).
%% Find files by extension, for example ".erl", avoiding resource fork
%% files in OS X. Such files are named for example src/._xyz.erl
%% Such files may also appear with network filesystems on OS X.
%%
%% The Ext is really a regexp, with any leading dot implicitly
%% escaped, and anchored at the end of the string.
%%
%% @spec find_files_by_ext(atom() | [atom() | [any()] | char()],string()) -> any().
find_files_by_ext(Dir, Ext) ->
find_files_by_ext(Dir, Ext, true).
%% @spec find_files_by_ext(atom() | [atom() | [any()] | char()],string(),boolean()) -> any().
find_files_by_ext(Dir, Ext, Recursive) ->
%% Convert simple extension to proper regex
EscapeDot = case Ext of
"." ++ _ ->
"\\";
_ ->
%% allow for other suffixes, such as _pb.erl
""
end,
ExtRe = "^[^._].*" ++ EscapeDot ++ Ext ++ [$$],
find_files(Dir, ExtRe, Recursive).
%% @spec now_str() -> [any()].
now_str() ->
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(),
lists:flatten(io_lib:format("~4b/~2..0b/~2..0b ~2..0b:~2..0b:~2..0b",
[Year, Month, Day, Hour, Minute, Second])).
%% @spec get_sub_dirs(atom() | binary() | [atom() | [any()] | char()]) -> [any()].
get_sub_dirs(Dir) ->
lists:filtermap(fun(SubDir) ->
case filelib:is_dir(SubDir) of
true -> {true, SubDir};
false -> false
end
end, filelib:wildcard(filename:join(Dir, "*"))).
%% @spec appup_plugin_appinfo(maybe_improper_list()) -> any().
appup_plugin_appinfo(Apps) ->
appup_plugin_appinfo(Apps, undefined).
%% @spec appup_plugin_appinfo(maybe_improper_list(),_) -> any().
appup_plugin_appinfo([], AppInfo) -> AppInfo;
appup_plugin_appinfo([AppInfo | Rest], _) ->
case rebar_app_info:name(AppInfo) of
<<"rebar3_appup_plugin">> ->
appup_plugin_appinfo([], AppInfo);
_ ->
appup_plugin_appinfo(Rest, undefined)
end.
%% @spec get_abstract_code(atom(),binary() | string()) -> 'encrypted_abstract_code' | 'no_abstract_code' | binary() | [{atom() | integer(),_} | {atom(),atom() | byte(),integer()} | {non_neg_integer(),atom() | tuple(),atom(),byte()}] | {atom(),[any()] | {atom() | tuple(),[any()]}} | {'error','beam_lib',{'not_a_beam_file',string()} | {'file_error',string(),atom() | nonempty_string() | non_neg_integer()} | {'invalid_beam_file',string(),atom() | nonempty_string() | non_neg_integer()} |
%% {'invalid_chunk',string(),atom() | nonempty_string() | non_neg_integer()} | {'missing_chunk',string(),atom() | nonempty_string() | non_neg_integer()} | {'unknown_chunk',string(),atom() | nonempty_string() | non_neg_integer()} | {'chunk_too_big',string(),nonempty_string(),non_neg_integer(),non_neg_integer()}}.
get_abstract_code(Module, Beam) ->
case beam_lib:chunks(Beam, [abstract_code]) of
{ok, {Module, [{abstract_code, AbstractCode}]}} ->
AbstractCode;
{error, beam_lib, {key_missing_or_invalid, _, _}} ->
encrypted_abstract_code;
Error -> Error
end.
tmp_filename() ->
lists:flatten(io_lib:format("tmp.appup.~p", [erlang:phash2(make_ref())])).
%% @spec find_app_info(_,_) -> any().
find_app_info(Name, State) ->
Apps = rebar_state:project_apps(State),
find_app_info1(Name, Apps).
%% @spec find_app_info1(_,maybe_improper_list()) -> any().
find_app_info1(_Name, []) -> undefined;
find_app_info1(Name, [App | Apps]) ->
case rebar_app_info:name(App) =:= Name of
true -> App;
false -> find_app_info1(Name, Apps)
end.
%% @spec load_module_from_beam(atom() | binary() | [atom() | [any()] | char()],atom()) -> {'module',atom()}.
load_module_from_beam(Beam, Module) ->
true = code:add_path(filename:dirname(Beam)),
{module, Module} = code:load_file(Module).
%% @spec unload_module_from_beam(atom() | binary() | [atom() | [any()] | char()],atom()) -> 'ok'.
unload_module_from_beam(Beam, Module) ->
code:del_path(filename:dirname(Beam)),
code:delete(Module),
code:purge(Module),
ok.
%% @spec beam_rel_path([atom() | [any()] | char()],atom() | binary() | [atom() | [any()] | char()],[atom() | [any()] | char()],[atom() | [any()] | char()]) -> binary() | string().
beam_rel_path(App, RelDir, Version, Module) ->
filename:join([RelDir, App, "lib",
App ++ "-" ++ Version,
"ebin",
Module ++ ".beam"]).
%% @spec beam_rel_path(atom() | binary() | [atom() | [any()] | char()],[atom() | [any()] | char()]) -> binary() | string().
beam_rel_path(EbinDir, Module) ->
filename:join([EbinDir,
Module ++ ".beam"]).
find_app_by_name(Name, Apps) ->
ec_lists:find(fun(App) ->
rebar_app_info:name(App) =:= Name
end, Apps).