Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grisp pack command #28

Merged
merged 2 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
ziopio marked this conversation as resolved.
Show resolved Hide resolved
]).
-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.
Loading