Skip to content

Commit

Permalink
Merge pull request #28 from grisp/sylane/pack-command
Browse files Browse the repository at this point in the history
Add grisp pack command
  • Loading branch information
sylane authored Sep 6, 2024
2 parents 0397642 + fd0a537 commit 7f61278
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to
actions will **NOT** be called if the actions do not raise any exception.
[#25](https://github.com/grisp/grisp_tools/pull/25)
- Add a firmware command to generate GRiSP 2 binary firmwares: [#26](https://github.com/grisp/grisp_tools/pull/26)
- Add a pack command to generate a GRiSP 2 software update package: [#28](https://github.com/grisp/grisp_tools/pull/28)

### Changed

Expand Down
3 changes: 2 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
{mapz, "~> 2.2"},
bbmustache,
hackney,
edifa
edifa,
grisp_update_packager
]}.
54 changes: 33 additions & 21 deletions rebar.lock
Original file line number Diff line number Diff line change
@@ -1,41 +1,53 @@
{"1.2.0",
[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.12.2">>},0},
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},1},
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.12.0">>},1},
{<<"edifa">>,{pkg,<<"edifa">>,<<"1.0.0">>},0},
{<<"erlexec">>,{pkg,<<"erlexec">>,<<"2.0.7">>},1},
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.18.1">>},0},
{<<"grisp_update_packager">>,{pkg,<<"grisp_update_packager">>,<<"1.0.0">>},0},
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.20.1">>},0},
{<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},1},
{<<"mapz">>,{pkg,<<"mapz">>,<<"2.2.0">>},0},
{<<"mapz">>,{pkg,<<"mapz">>,<<"2.4.0">>},0},
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1},
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},1},
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},1},
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1},
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}]}.
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.3.0">>},1},
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.4.1">>},1},
{<<"quickrand">>,{pkg,<<"quickrand">>,<<"2.0.7">>},2},
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.7">>},1},
{<<"termseal">>,{pkg,<<"termseal">>,<<"0.1.1">>},1},
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
{<<"uuid">>,{pkg,<<"uuid_erl">>,<<"2.0.7">>},1}]}.
[
{pkg_hash,[
{<<"bbmustache">>, <<"0CABDCE0DB9FE6D3318131174B9F2B351328A4C0AFBEB3E6E99BB0E02E9B621D">>},
{<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>},
{<<"certifi">>, <<"2D1CCA2EC95F59643862AF91F001478C9863C2AC9CB6E2F89780BFD8DE987329">>},
{<<"edifa">>, <<"0F1A01A0C79B7135F334B3FCEEB624F0574C5ED3E4554B06C8664AADA6A339C8">>},
{<<"erlexec">>, <<"76D0BC7487929741B5BB9F74DA2AF5DAF1492134733CF9A05C7AAA278B6934C5">>},
{<<"hackney">>, <<"F48BF88F521F2A229FC7BAE88CF4F85ADC9CD9BCF23B5DC8EB6A1788C662C4F6">>},
{<<"grisp_update_packager">>, <<"0532CCD0955398FAC4E1DE90FE85DB941CA609A2F4E066CFFE01ECE41DCCE119">>},
{<<"hackney">>, <<"8D97AEC62DDDDD757D128BFD1DF6C5861093419F8F7A4223823537BAD5D064E2">>},
{<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>},
{<<"mapz">>, <<"81EE5AD249BBC9427DAA6C3EE5166F88A448C5B0B2F5BBEE0495266308AFF2BA">>},
{<<"mapz">>, <<"77A8E38B69BAB16C5D3EBD44E6C619F8AF1F1598B0CAAE301D266605A0865756">>},
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
{<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>},
{<<"parse_trans">>, <<"16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8">>},
{<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>},
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
{<<"mimerl">>, <<"D0CD9FC04B9061F82490F6581E0128379830E78535E017F7780F37FEA7545726">>},
{<<"parse_trans">>, <<"6E6AA8167CB44CC8F39441D05193BE6E6F4E7C2946CB2759F015F8C56B76E5FF">>},
{<<"quickrand">>, <<"D2BD76676A446E6A058D678444B7FDA1387B813710D1AF6D6E29BB92186C8820">>},
{<<"ssl_verify_fun">>, <<"354C321CF377240C7B8716899E182CE4890C5938111A1296ADD3EC74CF1715DF">>},
{<<"termseal">>, <<"C9D93D4FF638EE99F9377D3438FC7AD132D2901EBBAF10C54F8DEA1D7E24D61C">>},
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>},
{<<"uuid">>, <<"B2078D2CC814F53AFA52D36C91E08962C7E7373585C623F4C0EA6DFB04B2AF94">>}]},
{pkg_hash_ext,[
{<<"bbmustache">>, <<"688B33A4D5CC2D51F575ADF0B3683FC40A38314A2F150906EDCFC77F5B577B3B">>},
{<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>},
{<<"certifi">>, <<"EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C">>},
{<<"edifa">>, <<"A1E010561E7D236A24C668D95626BE2BFE082ED0331CE1E6798BE0CD43F59A7B">>},
{<<"erlexec">>, <<"AF2DD940BB8E32F5AA40A65CB455DCAA18F5334FD3507E9BFD14A021E9630897">>},
{<<"hackney">>, <<"A4ECDAFF44297E9B5894AE499E9A070EA1888C84AFDD1FD9B7B2BC384950128E">>},
{<<"grisp_update_packager">>, <<"47BFDF6FADBED4B8342205A812198CF913E0223A98A775CAAE5D2FB5D5CF751C">>},
{<<"hackney">>, <<"FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3">>},
{<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>},
{<<"mapz">>, <<"861A878A86AB7E5897A9B57B3CA0B188FB53CE2935B153872C61F6641E709AA8">>},
{<<"mapz">>, <<"4B68DF5CF0522E0D6545DF7B681BC052865CDB78405AD4CC9C55FE45EE7B25BE">>},
{<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>},
{<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>},
{<<"parse_trans">>, <<"07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B">>},
{<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>},
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
{<<"mimerl">>, <<"A1E15A50D1887217DE95F0B9B0793E32853F7C258A5CD227650889B38839FE9D">>},
{<<"parse_trans">>, <<"620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A">>},
{<<"quickrand">>, <<"B8ACBF89A224BC217C3070CA8BEBC6EB236DBE7F9767993B274084EA044D35F0">>},
{<<"ssl_verify_fun">>, <<"FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8">>},
{<<"termseal">>, <<"466280936214AF1894FC431642E83341B7D13580A3F3485820A2D300C5CAEB49">>},
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>},
{<<"uuid">>, <<"4E4C5CA3461DC47C5E157ED42AA3981A053B7A186792AF972A27B14A9489324E">>}]}
].
3 changes: 3 additions & 0 deletions src/grisp_tools.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
-export([report/1]).
-export([configure/1]).
-export([firmware/1]).
-export([pack/1]).

%--- API -----------------------------------------------------------------------

Expand All @@ -33,3 +34,5 @@ report(Opts) -> grisp_tools_report:run(Opts).
configure(Opts) -> grisp_tools_configure:run(Opts).

firmware(Opts) -> grisp_tools_firmware:run(Opts).

pack(Opts) -> grisp_tools_pack:run(Opts).
21 changes: 21 additions & 0 deletions src/grisp_tools.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-ifndef(GRISP_TOOLS_HRL).
-define(GRISP_TOOLS_HRL, true).

-define(MiB, * 1024 * 1024).
-define(DOCKER_TOOLCHAIN_ROOT, "/grisp2-rtems-toolchain/rtems/5").
-define(GRISP2_BOOTLOADER_FILENAMES, [
"barebox-phytec-phycore-imx6ull-emmc-512mb.img",
"barebox-phytec-phycore-imx6ul-emmc-512mb.img"
]).
-define(GRISP2_RESERVED_SIZE, (4?MiB)).
-define(GRISP2_SYSTEM_SIZE, (256?MiB)).
-define(GRISP2_IMAGE_SIZE, (?GRISP2_RESERVED_SIZE + 2 * ?GRISP2_SYSTEM_SIZE)).
-define(GRISP2_PARTITIONS, [
#{role => system, type => fat32, size => ?GRISP2_SYSTEM_SIZE, start => ?GRISP2_RESERVED_SIZE},
#{role => system, type => fat32, size => ?GRISP2_SYSTEM_SIZE}
]).
-define(GRISP2_FAT_TYPE, 32).
-define(GRISP2_FAT_CLUSTER_SIZE, 4).


-endif. % GRISP_TOOLS_HRL
23 changes: 2 additions & 21 deletions src/grisp_tools_firmware.erl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-module(grisp_tools_firmware).

-include("grisp_tools.hrl").
-include_lib("kernel/include/file.hrl").

% API
Expand All @@ -10,26 +11,6 @@
-import(grisp_tools_util, [shell/3]).


%--- MACROS --------------------------------------------------------------------

-define(MiB, * 1024 * 1024).
-define(DOCKER_TOOLCHAIN_ROOT, "/grisp2-rtems-toolchain/rtems/5").
-define(GRISP2_BOOTLOADER_FILENAMES, [
"foobar.img",
"barebox-phytec-phycore-imx6ull-emmc-512mb.img",
"barebox-phytec-phycore-imx6ul-emmc-512mb.img"
]).
-define(GRISP2_RESERVED_SIZE, (4?MiB)).
-define(GRISP2_SYSTEM_SIZE, (256?MiB)).
-define(GRISP2_IMAGE_SIZE, (?GRISP2_RESERVED_SIZE + 2 * ?GRISP2_SYSTEM_SIZE)).
-define(GRISP2_PARTITIONS, [
#{type => fat32, size => ?GRISP2_SYSTEM_SIZE,
start => ?GRISP2_RESERVED_SIZE},
#{type => fat32, size => ?GRISP2_SYSTEM_SIZE}
]).
-define(GRISP2_FAT_TYPE, 32).
-define(GRISP2_FAT_CLUSTER_SIZE, 4).

%--- API -----------------------------------------------------------------------

run(State) ->
Expand Down Expand Up @@ -363,7 +344,7 @@ docker_check_image(State, ImageName) ->

docker_export(State, ImageName, InPath, OutDir) ->
ok = grisp_tools_util:ensure_dir(OutDir),
Command = ["docker run --volume ", OutDir, ":", OutDir,
Command = ["docker run --rm --volume ", OutDir, ":", OutDir,
" ", ImageName, " sh -c \"cd ", OutDir,
" && cp -rf '", InPath, "' .\""],
case shell(State, Command, [return_on_error]) of
Expand Down
176 changes: 176 additions & 0 deletions src/grisp_tools_pack.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
-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.

0 comments on commit 7f61278

Please sign in to comment.