%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*- %%% Copyright 2014 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 Tristan Sloughter %%% @copyright (C) 2014 Erlware, LLC. %%% %%% @doc Given a complete built release this provider assembles that release %%% into a release directory. -module(rlx_prv_archive). -behaviour(provider). -export([init/1, do/1, format_error/1]). -include("relx.hrl"). -define(PROVIDER, tar). -define(DEPS, [resolve_release]). %%============================================================================ %% API %%============================================================================ -spec init(rlx_state:t()) -> {ok, rlx_state:t()}. init(State) -> State1 = rlx_state:add_provider(State, providers:create([{name, ?PROVIDER}, {module, ?MODULE}, {deps, ?DEPS}])), {ok, State1}. -spec do(rlx_state:t()) -> {ok, rlx_state:t()} | relx:error(). do(State) -> {RelName, RelVsn} = rlx_state:default_configured_release(State), Release = rlx_state:get_realized_release(State, RelName, RelVsn), OutputDir = rlx_state:output_dir(State), make_tar(State, Release, OutputDir). format_error({tar_unknown_generation_error, Module, Vsn}) -> io_lib:format("Tarball generation error of ~s ~s", [Module, Vsn]); format_error({tar_generation_warn, Module, Warnings}) -> io_lib:format("Tarball generation warnings for ~p : ~p", [Module, Warnings]); format_error({tar_generation_error, Module, Errors}) -> io_lib:format("Tarball generation error for ~p reason ~p", [Module, Errors]). make_tar(State, Release, OutputDir) -> Name = atom_to_list(rlx_release:name(Release)), Vsn = rlx_release:vsn(Release), ErtsVersion = rlx_release:erts(Release), Opts = [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]}, {dirs, [include | maybe_src_dirs(State)]}, {outdir, OutputDir} | case rlx_state:get(State, include_erts, true) of true -> Prefix = code:root_dir(), ErtsDir = filename:join([Prefix]), [{erts, ErtsDir}]; false -> []; Prefix -> ErtsDir = filename:join([Prefix]), [{erts, ErtsDir}] end], case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]), Opts) of ok -> TempDir = ec_file:insecure_mkdtemp(), try update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) catch E:R -> ec_file:remove(TempDir, [recursive]), ?RLX_ERROR({tar_generation_error, E, R}) end; {ok, Module, Warnings} -> ?RLX_ERROR({tar_generation_warn, Module, Warnings}); error -> ?RLX_ERROR({tar_unknown_generation_error, Name, Vsn}); {error, Module, Errors} -> ?RLX_ERROR({tar_generation_error, Module, Errors}) end. update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) -> IncludeErts = rlx_state:get(State, include_erts, true), SystemLibs = rlx_state:get(State, system_libs, true), {RelName, RelVsn} = rlx_state:default_configured_release(State), Release = rlx_state:get_realized_release(State, RelName, RelVsn), TarFile = filename:join(OutputDir, Name++"-"++Vsn++".tar.gz"), file:rename(filename:join(OutputDir, Name++".tar.gz"), TarFile), erl_tar:extract(TarFile, [{cwd, TempDir}, compressed]), OverlayVars = rlx_prv_overlay:generate_overlay_vars(State, Release), OverlayFiles = overlay_files(OverlayVars, rlx_state:get(State, overlay, undefined), OutputDir), ConfigFiles = config_files(Vsn, OutputDir), ok = erl_tar:create(TarFile, [{"releases", filename:join(TempDir, "releases")}, {filename:join(["releases", "start_erl.data"]), filename:join([OutputDir, "releases", "start_erl.data"])}, {filename:join(["releases", "RELEASES"]), filename:join([OutputDir, "releases", "RELEASES"])}, {"bin", filename:join([OutputDir, "bin"])} | case IncludeErts of false -> %% Remove system libs from tarball case SystemLibs of false -> Libs = filelib:wildcard("*", filename:join(TempDir, "lib")), AllSystemLibs = filelib:wildcard("*", code:lib_dir()), [{filename:join("lib", LibDir), filename:join([TempDir, "lib", LibDir])} || LibDir <- lists:subtract(Libs, AllSystemLibs)]; _ -> [{"lib", filename:join(TempDir, "lib")}] end; _ -> [{"lib", filename:join(TempDir, "lib")}, {"erts-"++ErtsVersion, filename:join(OutputDir, "erts-"++ErtsVersion)}] end]++ConfigFiles++OverlayFiles, [dereference,compressed]), ec_cmd_log:info(rlx_state:log(State), "tarball ~s successfully created!~n", [TarFile]), ec_file:remove(TempDir, [recursive]), {ok, State}. config_files(Vsn, OutputDir) -> VMArgs = {filename:join(["releases", Vsn, "vm.args"]), filename:join([OutputDir, "releases", Vsn, "vm.args"])}, VMArgsSrc = {filename:join(["releases", Vsn, "vm.args.src"]), filename:join([OutputDir, "releases", Vsn, "vm.args.src"])}, VMArgsOrig = {filename:join(["releases", Vsn, "vm.args.orig"]), filename:join([OutputDir, "releases", Vsn, "vm.args.orig"])}, SysConfigOrig = {filename:join(["releases", Vsn, "sys.config.orig"]), filename:join([OutputDir, "releases", Vsn, "sys.config.orig"])}, [{NameInArchive, Filename} || {NameInArchive, Filename} <- [VMArgsSrc, VMArgs, VMArgsOrig, SysConfigOrig], filelib:is_file(Filename)]. overlay_files(_, undefined, _) -> []; overlay_files(OverlayVars, Overlay, OutputDir) -> [begin To = to(O), File = rlx_prv_overlay:render_string(OverlayVars, To), {ec_cnv:to_list(File), ec_cnv:to_list(filename:join(OutputDir, File))} end || O <- Overlay, filter(O)]. to({link, _, To}) -> To; to({copy, _, To}) -> To; to({mkdir, To}) -> To; to({template, _, To}) -> To. filter({_, _, "bin/"++_}) -> false; filter({link, _, _}) -> true; filter({copy, _, _}) -> true; filter({mkdir, _}) -> true; filter({template, _, _}) -> true; filter(_) -> false. maybe_src_dirs(State) -> case rlx_state:get(State, include_src, true) of false -> []; true -> [src] end.