Current section
Files
Jump to
Current section
Files
src/grisp_tools_pack.erl
-module(grisp_tools_pack).
-include("grisp_tools.hrl").
-include_lib("kernel/include/file.hrl").
% API
-export([run/1]).
-import(grisp_tools_util, [event/2]).
%--- API -----------------------------------------------------------------------
run(State) ->
grisp_tools_util:weave(State, [
fun grisp_tools_step:config/1,
{pack, [
fun prepare/1,
fun package/1,
fun cleanup/1
]}
]).
%--- Tasks ---------------------------------------------------------------------
prepare(State) ->
Force = maps:get(force, State, false),
State2 = State#{force => Force},
grisp_tools_util:weave(State2, [
fun validate_temp_dir/1,
fun validate_system/1,
fun validate_bootloader/1,
fun validate_package/1
]).
validate_temp_dir(State) ->
case maps:find(temp_dir, State) of
{ok, TempDir} when TempDir =/= undefined ->
case filelib:is_dir(TempDir) of
true -> State;
false -> event(State, [{error, directory_not_found, TempDir}])
end;
_ ->
{Output, State2} = shell(State, "mktemp -d", []),
TempDir = string:trim(Output),
case filelib:is_dir(TempDir) of
true ->
State2#{temp_dir => TempDir,
cleanup_temp_dir => true};
false ->
event(State2, [{error, directory_not_found, TempDir}])
end
end.
validate_system(State) ->
case maps:find(system, State) of
{ok, SysPath} when SysPath =/= undefined ->
case filelib:is_file(SysPath) of
true -> State;
false ->
event(State, [{error, system_not_found, SysPath}])
end;
_ ->
event(State, [{error, missing_parameter, system}])
end.
validate_bootloader(State = #{bootloader := BootPath})
when BootPath =/= undefined ->
case filelib:is_file(BootPath) of
true -> State;
false ->
event(State, [{error, bootloader_not_found, BootPath}])
end;
validate_bootloader(State) ->
State#{bootloader => undefined}.
validate_package(State) ->
case maps:find(package, State) of
{ok, PackageFile} when PackageFile =/= undefined ->
prepare_output_file(State, PackageFile);
_ ->
event(State, [{error, missing_parameter, package}])
end.
package(State) ->
grisp_tools_util:weave(State, [
fun expand_bootloader/1,
fun expand_system/1,
fun build_package/1,
fun cleanup/1
], [
fun cleanup/1
]).
expand_bootloader(State = #{bootloader := BootPath})
when BootPath =/= undefined ->
{ExpPath, State2} = maybe_expand(State, BootPath),
State2#{bootloader => ExpPath};
expand_bootloader(State) ->
State.
expand_system(State = #{system := SysPath})
when SysPath =/= undefined ->
{ExpPath, State2} = maybe_expand(State, SysPath),
State2#{system => ExpPath};
expand_system(State) ->
State.
build_package(State = #{package := PackageFile}) ->
PackagerOpts1 = maps:with([name, version, block_size,
key_file, system, bootloader], State),
PackagerOpts2 = PackagerOpts1#{
tarball => true,
mbr => ?GRISP2_PARTITIONS
},
case grisp_update_packager:package(PackageFile, PackagerOpts2) of
ok -> event(State, [{done, PackageFile}]);
{error, Reason} -> event(State, [{error, Reason}])
end.
cleanup(State) ->
cleanup_temp_dir(State).
cleanup_temp_dir(State = #{temp_dir := TempDir, cleanup_temp_dir := true}) ->
{_, State2} = shell(State, "rm -rf '~s'", [TempDir]),
State2#{cleanup_temp_dir => false};
cleanup_temp_dir(State) ->
State.
%--- Internal ------------------------------------------------------------------
shell(State, Fmt, Args) ->
Cmd = binary_to_list(iolist_to_binary(io_lib:format(Fmt, Args))),
{{ok, Output}, State2} = grisp_tools_util:shell(State, Cmd),
{Output, State2}.
is_compressed(Path) ->
Ext = <<".gz">>,
case binary:matches(Path, Ext) of
[{Pos, _Length}] when Pos + byte_size(Ext) =:= byte_size(Path) ->
NoExt = binary:part(Path, 0, byte_size(Path) - byte_size(Ext)),
{true, filename:basename(NoExt)};
_ ->
{false, filename:basename(Path)}
end.
maybe_expand(State = #{temp_dir := TempDir}, Path) ->
case is_compressed(Path) of
{false, _} -> {Path, State};
{true, ExpName} ->
CompName = filename:basename(Path),
State2 = event(State, [{expanding, CompName}]),
TempCompPath = filename:join([TempDir, CompName]),
TempExpPath = filename:join([TempDir, ExpName]),
{_, State2} = shell(State, "cp '~s' '~s'", [Path, TempDir]),
{_, State3} = shell(State2, "gunzip '~s'", [TempCompPath]),
{TempExpPath, State3}
end.
prepare_output_file(State, Filepath) ->
Force = maps:get(force, State, false),
case file:read_file_info(Filepath) of
{ok, #file_info{}} when Force =:= false ->
event(State, [{error, file_exists, Filepath}]);
{ok, #file_info{type = regular}} when Force =:= true->
case file:delete(Filepath) of
ok -> State;
{error, _Reason} ->
event(State, [{error, file_access, Filepath}])
end;
{ok, #file_info{type = regular}} ->
event(State, [{error, not_a_file, Filepath}]);
{error, enoent} ->
State
end.