Packages
relx
3.33.0
4.10.0
4.9.0
4.8.0
4.7.0
4.6.0
4.5.0
4.4.0
4.3.0
4.2.0
4.1.0
4.0.2
4.0.1
4.0.0
4.0.0-rc2
3.33.0
3.32.1
3.32.0
3.31.0
3.30.0
3.29.0
3.28.0
3.27.0
3.26.0
3.25.0
3.24.4
3.24.3
3.24.2
3.24.1
3.23.1
3.23.0
3.22.4
3.22.3
retired
3.22.2
3.22.1
retired
3.22.0
3.21.1
3.21.0
3.20.0
3.19.0
3.18.0
3.17.0
3.16.0
3.15.0
3.14.0
3.13.0
3.12.0
3.11.0
3.10.0
3.9.0
3.8.0
3.7.1
3.7.0
3.6.0
3.5.0
3.4.0
3.3.2
3.3.1
3.3.0
3.2.0
3.1.0
3.0.0
2.1.1
2.1.0
Release assembler for Erlang/OTP Releases
Current section
Files
Jump to
Current section
Files
src/rlx_util.erl
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
%%% Copyright 2012 Erlware, LLC. 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.
%%%---------------------------------------------------------------------------
%%% @author Eric Merritt <ericbmerritt@gmail.com>
%%% @copyright (C) 2012 Erlware, LLC.
%%%
%%% @doc Trivial utility file to help handle common tasks
-module(rlx_util).
-export([get_code_paths/2,
release_output_dir/2,
make_script/2,
mkdir_p/1,
to_binary/1,
to_string/1,
to_atom/1,
is_error/1,
error_reason/1,
indent/1,
optional_to_string/1,
wildcard_paths/1,
render/1,
render/2,
load_file/3,
template_files/0,
escript_foldl/3,
intensity/0,
symlink_or_copy/2]).
-export([os_type/1]).
-define(DFLT_INTENSITY, high).
-define(ONE_LEVEL_INDENT, " ").
-include_lib("kernel/include/file.hrl").
%%============================================================================
%% types
%%============================================================================
%%============================================================================
%% API
%%============================================================================
%% @doc Generates the correct set of code paths for the system.
-spec get_code_paths(rlx_release:t(), file:name()) -> [file:name()].
get_code_paths(Release, OutDir) ->
LibDir = filename:join(OutDir, "lib"),
[filename:join([LibDir,
erlang:atom_to_list(rlx_app_info:name(App)) ++ "-" ++
rlx_app_info:original_vsn(App), "ebin"]) ||
App <- rlx_release:application_details(Release)].
-spec release_output_dir(rlx_state:t(), rlx_release:t()) -> string().
release_output_dir(State, Release) ->
OutputDir = rlx_state:output_dir(State),
filename:join([OutputDir,
"releases",
rlx_release:vsn(Release)]).
-spec make_script([term()],
fun(([term()]) -> Res)) -> Res.
make_script(Options, RunFun) ->
%% Erts 5.9 introduced a non backwards compatible option to
%% erlang this takes that into account
Erts = erlang:system_info(version),
case ec_semver:gte(Erts, "5.9") of
true ->
RunFun([no_warn_sasl | Options]);
_ ->
RunFun(Options)
end.
%% @doc Makes a directory including parent dirs if they are missing.
-spec mkdir_p(string()) -> ok | {error, Reason::file:posix()}.
mkdir_p(Path) ->
%% We are exploiting a feature of ensuredir that that creates all
%% directories up to the last element in the filename, then ignores
%% that last element. This way we ensure that the dir is created
%% and not have any worries about path names
DirName = filename:join([filename:absname(Path), "tmp"]),
filelib:ensure_dir(DirName).
%% @doc ident to the level specified
-spec indent(non_neg_integer()) -> iolist().
indent(Amount) when erlang:is_integer(Amount) ->
[?ONE_LEVEL_INDENT || _ <- lists:seq(1, Amount)].
-spec to_binary(iolist() | binary()) -> binary().
to_binary(String) when erlang:is_list(String) ->
erlang:iolist_to_binary(String);
to_binary(Bin) when erlang:is_binary(Bin) ->
Bin.
-spec to_string(binary() | string() | atom()) -> string().
to_string(Binary) when erlang:is_binary(Binary) ->
erlang:binary_to_list(Binary);
to_string(Atom) when erlang:is_atom(Atom) ->
erlang:atom_to_list(Atom);
to_string(Else) when erlang:is_list(Else) ->
Else.
-spec to_atom(atom() | string() | binary()) -> atom().
to_atom(Binary)
when erlang:is_binary(Binary) ->
erlang:list_to_atom(to_string(Binary));
to_atom(String)
when erlang:is_list(String) ->
erlang:list_to_atom(String);
to_atom(Atom)
when erlang:is_atom(Atom) ->
Atom.
%% @doc get the reason for a particular relx error
-spec error_reason(relx:error()) -> any().
error_reason({error, {_, Reason}}) ->
Reason.
%% @doc check to see if the value is a relx error
-spec is_error(relx:error() | any()) -> boolean().
is_error({error, _}) ->
true;
is_error(_) ->
false.
%% @doc convert optional argument to empty string if undefined
optional_to_string(undefined) ->
"";
optional_to_string(Value) when is_list(Value) ->
case io_lib:printable_list(Value) of
true ->
Value;
false ->
""
end.
%% @doc expand wildcards and names in the given paths
-spec wildcard_paths([file:filename_all()]) -> [string()].
wildcard_paths(Paths) ->
[filename:absname(Expanded) || Path <- Paths, Expanded <- wildcard(Path)].
%% In case the given directory does not expand,
%% we return it back in a list so we trigger the
%% proper error reportings.
-spec wildcard(file:filename_all()) -> [string()].
wildcard(Path) when is_binary(Path) ->
wildcard(binary_to_list(Path));
wildcard(Path) when is_list(Path) ->
case filelib:wildcard(Path) of
[] -> [Path];
Paths -> Paths
end.
-spec render(binary() | iolist()) ->
{ok, binary()} | {error, render_failed}.
render(Template) ->
render(Template, []).
-spec render(binary() | iolist(), proplists:proplist()) ->
{ok, binary()} | {error, render_failed}.
render(Template, Data) when is_list(Template) ->
render(ec_cnv:to_binary(Template), Data);
render(Template, Data) when is_binary(Template) ->
case catch bbmustache:render(Template, Data,
[{key_type, atom}]) of
Bin when is_binary(Bin) -> {ok, Bin};
_ -> {error, render_failed}
end.
load_file(Files, escript, Name) ->
{Name, Bin} = lists:keyfind(Name, 1, Files),
Bin;
load_file(_Files, file, Name) ->
{ok, Bin} = file:read_file(Name),
Bin.
template_files() ->
find_priv_templates() ++ escript_files().
find_priv_templates() ->
Files = filelib:wildcard(filename:join([code:priv_dir(relx), "templates", "*"])),
lists:map(fun(File) ->
{ok, Bin} = file:read_file(File),
{filename:basename(File), Bin}
end, Files).
%% Scan the current escript for available files
escript_files() ->
try
{ok, Files} = escript_foldl(
fun(Name, _, GetBin, Acc) ->
[{filename:basename(Name), GetBin()} | Acc]
end, [], filename:absname(escript:script_name())),
Files
catch
_:_ ->
[]
end.
escript_foldl(Fun, Acc, File) ->
case escript:extract(File, []) of
{ok, [_Shebang, _Comment, _EmuArgs, Body]} ->
case Body of
{source, BeamCode} ->
GetInfo = fun() -> file:read_file_info(File) end,
GetBin = fun() -> BeamCode end,
{ok, Fun(".", GetInfo, GetBin, Acc)};
{beam, BeamCode} ->
GetInfo = fun() -> file:read_file_info(File) end,
GetBin = fun() -> BeamCode end,
{ok, Fun(".", GetInfo, GetBin, Acc)};
{archive, ArchiveBin} ->
zip:foldl(Fun, Acc, {File, ArchiveBin})
end;
{error, _} = Error ->
Error
end.
symlink_or_copy(Source, Target) ->
case file:make_symlink(Source, Target) of
ok ->
ok;
{error, eexist} ->
{error, eexist};
{error, Err} ->
case {os:type(), Err} of
{{win32, _}, eperm} ->
% We get eperm on Windows if we do not have
% SeCreateSymbolicLinkPrivilege
% Try the next alternative
win32_make_junction_or_copy(Source, Target);
_ ->
% On other systems we try to copy next
cp_r(Source, Target)
end
end.
cp_r(Source, Target) ->
ec_file:copy(Source, Target, [{recursive, true}, {fileinfo, [mode, time, owner, group]}]).
win32_make_junction_or_copy(Source, Target) ->
case filelib:is_dir(Source) of
true ->
win32_make_junction(Source, Target);
_ ->
cp_r(Source, Target)
end.
win32_make_junction(Source, Target) ->
% The mklink will fail if the target already exists, check for that first
case file:read_link_info(Target) of
{error, enoent} ->
win32_make_junction_cmd(Source, Target);
{ok, #file_info{type = symlink}} ->
case file:read_link(Target) of
{ok, Source} ->
ok;
{ok, _} ->
ok = file:del_dir(Target),
win32_make_junction_cmd(Source, Target);
{error, Reason} ->
{error, {readlink, Reason}}
end;
{ok, #file_info{type = _Type}} ->
% Directory already exists, so we overwrite the copy
cp_r(Source, Target);
Error ->
Error
end.
win32_make_junction_cmd(Source, Target) ->
S = unicode:characters_to_list(Source),
T = unicode:characters_to_list(Target),
Cmd = "cmd /c mklink /j " ++ filename:nativename(T) ++ " " ++ filename:nativename(S),
case os:cmd(Cmd) of
"Junction created " ++ _ ->
ok;
[] ->
% When mklink fails it prints the error message to stderr which
% is not picked up by os:cmd() hence this case switch is for
% an empty message
cp_r(Source, Target)
end.
%% @doc Returns the color intensity, we first check the application envorinment
%% if that is not set we check the environment variable RELX_COLOR.
intensity() ->
case application:get_env(relx, color_intensity) of
undefined ->
R = case os:getenv("RELX_COLOR") of
"high" ->
high;
"low" ->
low;
_ ->
?DFLT_INTENSITY
end,
application:set_env(relx, color_intensity, R),
R;
{ok, Mode} ->
Mode
end.
os_type(State) ->
case include_erts_is_win32(State) of
true -> {win32,nt};
false -> os:type()
end.
include_erts_is_win32(State) ->
case rlx_state:get(State, include_erts, true) of
true -> false;
false -> false;
Path -> is_win32_erts(Path,State)
end.
is_win32_erts(Path,State) ->
case filelib:wildcard(filename:join([Path,"bin","erl.exe"])) of
[] -> false;
_ ->
ec_cmd_log:info(rlx_state:log(State),
"Including Erts is win32 ~n", []),
true
end.
%%%===================================================================
%%% Test Functions
%%%===================================================================