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

SEA-232 "rebar3 grisp configure" #78

Merged
merged 14 commits into from
May 3, 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
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,57 @@ Prerequisites:
To create a new GRiSP project:

```
rebar3 new grispapp name=mygrispproject dest=/path/to/SD-card
rebar3 grisp configure
```

This command will provide you with a CLI that will guide you in the creation of your GRiSP project.

You can also use the command in a non-interactive way:

```
rebar3 grisp configure -i false
```

Unless stated otherwise, the non-interactive option will use the default values to create the GRiSP project. You can overwrite the default values in the command:
```
rebar3 grisp configure -i false --name="my_grisp_app" -n true -w true --ssid="mywifi" --psk="wifipsk"
```
This command will create a new GRiSP project named "my_grisp_app" with a network (`-n true`) and wifi (`-w true`) configuration already setup. The configuration will use the ssid "mywifi" and the psk "wifipsk".

Note that some options require others. For example, if you want to setup the ssid of the wifi, then you also need to activate the network and wifi configuration (`-n true` and `-w true`).

The specific variables provided by this plug-in are:

* **`interactive`** activate the interactive mode
* **`name`** is the name of the OTP application
* **`dest`** is the destination path for deployment. This should point to where
your SD-card is mounted (e.g. on macOS it is `/Volumes/<NAME>` where `<NAME>`
is the name of the SD-card partition)
* **`otp_release`** is the target Erlang/OTP version used on the GRiSP board
* **`otp_version`** is the target Erlang/OTP version used on the GRiSP board
* **`network`** specifies if the project contains network configuration files
* **`wifi`** specifies if the project contains wifi configuration files. (requires `network`)
* **`ssid`** is the ssid of the wifi network you want your board to connect to. (requires `network` and `wifi`)
* **`psk`** is the psk of the wifi network you want your board to connect to. (requires `network` and `wifi`)
* **`grisp_io`** specifies if you want your board to connect and use GRiSP.io. (requires `network`)
* **`grisp_io_linking`** specifies if you want your board to link itself to GRiSP.io. (requires `network` and `grisp_io`)
* **`token`** is your personnal GRiSP.io token. (requires `network`, `grip_io` and `grisp_io_linking`)
* **`epmd`** specifies if you want your board to have epmd. (requires `network`)
* **`cookie`** is the magic cookie that your board should use. (requires `network` and `epmd`)

Some variables are modfiable only through the command line. These variables are:
* **`desc`** is the short description of the GRiSP application
* **`copyright_year`** is the copyright year
* **`author_name`** is the name of the author of the project
* **`author_email`** is the email of the author of the project

For a full list of customizable variables, run `rebar3 help grisp configure`.

For a full list of customizable variables, run `rebar3 new help grispapp`.
In the interactive mode you can also specify a few variables. During the CLI interaction, the questions related to these variable will be skipped. For example:

```
rebar3 grisp configure --name="mygrispapp"
```
Here the CLI won't ask you for the name of your GRiSP project because it's already provided.

## Compile the project

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{applications, [
kernel,
stdlib,
grisp
{{^grisp_io}}grisp{{/grisp_io}}{{#grisp_io}}grisp_connect{{/grisp_io}}
]},
{env,[]},
{modules, []},
Expand Down
30 changes: 30 additions & 0 deletions priv/templates/common/rebar.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{deps, [
{{^grisp_io}}grisp{{/grisp_io}}{{#grisp_io}}{grisp_connect, {git, "https://github.com/grisp/grisp_connect", {branch, "main"}}}{{/grisp_io}}{{^epmd}}
{{/epmd}}{{#epmd}},
{epmd, {git, "https://github.com/erlang/epmd", {ref, "4d1a59"}}}
{{/epmd}}
]}.

{erl_opts, [debug_info]}.

{plugins, [rebar3_grisp]}.

{grisp, [
{otp, [
{version, "{{otp_version}}"}
]},
{deploy, [
{destination, "{{dest}}"}
]}
]}.

{shell, [{apps, []}]}.

{relx, [
{release, {{{name}}, "0.1.0"}, [
{{#epmd}}
{epmd, none},
{{/epmd}}
{{name}}
]}
]}.
File renamed without changes.
7 changes: 7 additions & 0 deletions priv/templates/common/sys.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{{#grisp_io_linking}} {grisp_connect,[
{device_linking_token,<<"{{token}}">>}
]},
{{/grisp_io_linking}} {{{name}}, [
]}
].
14 changes: 0 additions & 14 deletions priv/templates/grispapp.template

This file was deleted.

13 changes: 13 additions & 0 deletions priv/templates/network/erl_inetrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
%--- Erlang Inet Configuration -------------------------------------------------

% Add hosts
% {host, {X,X,X,X}, ["Host"]}.

% Do not monitor the hosts file
{hosts_file, ""}.

% Disable caching
{cache_size, 0}.

% Specify lookup method
{lookup, [file, dns]}.
9 changes: 9 additions & 0 deletions priv/templates/network/grisp.ini.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{=<% %>=}}
[erlang]
args = erl.rtems -C multi_time_warp -- -mode embedded -home . -pa . -root {{release_name}} -bindir {{release_name}}/erts-{{erts_vsn}}/bin -boot {{release_name}}/releases/{{release_version}}/start -config {{release_name}}/releases/{{release_version}}/sys.config <%#network%>-kernel inetrc "./erl_inetrc"<%/network%> <%#epmd%>-internal_epmd epmd_sup -sname {{release_name}} -setcookie <%cookie%><%/epmd%>
shell = erlang

[network]
ip_self=dhcp
<%#wifi%>wlan=enable
wpa= wpa_supplicant.conf<%/wifi%>
5 changes: 5 additions & 0 deletions priv/templates/network/wpa_supplicant.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
network={
ssid="{{ssid}}"
key_mgmt=WPA-PSK
psk="{{psk}}"
}
20 changes: 0 additions & 20 deletions priv/templates/rebar.config

This file was deleted.

1 change: 1 addition & 0 deletions src/rebar3_grisp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ init(State) ->
lists:foldl(fun(Mod, {ok, S}) -> Mod:init(S) end, {ok, State}, [
rebar3_grisp_deploy,
rebar3_grisp_build,
rebar3_grisp_configure,
rebar3_grisp_package,
rebar3_grisp_version,
rebar3_grisp_report
Expand Down
212 changes: 212 additions & 0 deletions src/rebar3_grisp_configure.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
-module(rebar3_grisp_configure).

% Callbacks
-export([init/1]).
-export([do/1]).
-export([format_error/1]).

-import(rebar3_grisp_util, [
console/1,
console/2,
info/1,
info/2,
debug/1,
debug/2,
abort/2
]).

%--- Callbacks -----------------------------------------------------------------

-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Opts = lists:map(fun({_, {Key, Short}, Type, Descr}) ->
Long = atom_to_list(Key),
{Key, Short, Long, Type, Descr}
end, grisp_tools_configure:settings()),
Provider = providers:create([
{namespace, grisp},
{name, configure},
{module, ?MODULE},
{bare, true},
{example, "rebar3 grisp configure"},
{opts, Opts},
{profiles, [default]},
{short_desc, "Create and configure a new GRiSP app"},
{desc, "Create and configure a new GRiSP app with user provided values"}
]),
{ok, rebar_state:add_provider(State, Provider)}.

-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(RState) ->
{ok, _} = application:ensure_all_started(rebar3_grisp),

try
{Opts, _Rest} = rebar_state:command_parsed_args(RState),

Provider = providers:get_provider(configure,
rebar_state:providers(RState),
grisp),
ProviderOpts = lists:map(fun({Key, Short, Long, {Type, _}, Descr}) ->
{Key, Short, Long, Type, Descr}
end, providers:opts(Provider)),
CmdArgs = rebar_state:command_args(RState),
{ok, {UserOpts, _}} = getopt:parse(ProviderOpts,
lists:join(" ", CmdArgs)),
UserOptsMap = maps:from_list(UserOpts),

Config = rebar3_grisp_util:config(RState),

CustomBuild = rebar3_grisp_util:should_build(Config),
Board = rebar3_grisp_util:platform(Config),
Version = rebar3_grisp_util:otp_version(Config),

Apps = rebar3_grisp_util:apps(RState),

ProjectRoot = rebar_dir:root_dir(RState),

ReportDir = rebar3_grisp_util:report_dir(RState),

InitFlags = maps:from_list([
{Key, rebar3_grisp_util:get(Key, Opts, D)}
|| {_, {Key, _}, {_, D}, _} <-
grisp_tools_configure:settings()
]),
InitState = #{
project_root => ProjectRoot,
report_dir => ReportDir,
flags => InitFlags,
user_opts => UserOptsMap,
apps => Apps,
otp_version_requirement => Version,
custom_build => CustomBuild,
platform => Board,
handlers => grisp_tools:handlers_init(#{
event => {fun event_handler/2, #{}},
shell => {fun rebar3_grisp_handler:shell/3, #{}}
}),
binaries => []},


case OptsMap = maps:from_list(Opts) of
#{interactive := false} ->
check_custom_params(OptsMap, UserOptsMap);
_ -> ok
end,

State = grisp_tools:configure(InitState),

#{flags := Flags} = State,

Files = templater(Flags),

{ok, Cwd} = file:get_cwd(),
PrivDir = code:priv_dir(rebar3_grisp),
TemplatesDir = filename:join(PrivDir, "templates"),
lists:map(fun({From, To}) ->
FilePath = filename:join(TemplatesDir, From),
maybe_write_file(FilePath, filename:join(Cwd, To), Flags)
end, Files),

_ = grisp_tools:handlers_finalize(State),
{ok, RState}
catch
error:E ->
abort(
"Unexpected error: ~p~n",
[E]
)
end.

-spec format_error(any()) -> iolist().
format_error(Reason) ->
io_lib:format("~p", [Reason]).


%--- Internal ------------------------------------------------------------------
event_handler(Event, State) ->
event(Event),
{ok, State}.
event({say, Prompt}) ->
console(Prompt);
event({info, Prompt}) ->
info(Prompt);
event(_) ->
info("Unexpected event").

check_custom_params(OptsMap, _)
when not map_get(network, OptsMap) andalso
(map_get(wifi, OptsMap) orelse
map_get(grisp_io, OptsMap) orelse
map_get(epmd, OptsMap)) ->
abort("The network configuration needs to be enabled with
'--network=true' or '-n true' if you want to setup
either wifi, grisp.io or epmd in non-interactive mode", []);
check_custom_params(OptsMap, UserOptsMap)
when not map_get(wifi, OptsMap) andalso
(is_map_key(ssid, UserOptsMap) orelse is_map_key(psk, UserOptsMap)) ->
abort("The wifi configuration needs to be enabled with
'--wifi=true' or '-w true' if you want to setup the ssid or the psk",
[]);
check_custom_params(OptsMap, UserOptsMap)
when not map_get(grisp_io, OptsMap) andalso
is_map_key(grisp_io_linking, UserOptsMap) ->
abort("The grisp.io integration needs to be enabled with
'--grisp_io=true' or '-g true' if you want to activate the linking",
[]);
check_custom_params(OptsMap, UserOptsMap)
when not map_get(grisp_io_linking, OptsMap) andalso is_map_key(token, UserOptsMap) ->
abort("The grisp.io linking configuration needs to be enabled with
'--grisp_io_linking=true' or '-l true' if you want to setup the token",
[]);
check_custom_params(OptsMap, UserOptsMap)
when not map_get(epmd, OptsMap) andalso is_map_key(cookie, UserOptsMap) ->
abort("The epmd configuration needs to be enabled with
'--epmd=true' or '-e true' if you want to setup the cookie",
[]);
check_custom_params(_, _) ->
ok.

-spec templater(map()) -> [{Src, Dest}] when
Src :: string(),
Dest :: string().
templater(#{name := Name} = Flags) ->
[{"common/app.erl", Name ++ "/src/" ++ Name ++ ".erl"},
{"common/sup.erl", Name ++ "/src/" ++ Name ++ "_sup.erl"},
{"common/gitignore", Name ++ "/.gitignore"},
{"common/LICENSE", Name ++ "/LICENSE"},
{"common/README.md", Name ++ "/README.md"},
{"common/otp_app.app.src", Name ++ "/src/" ++ Name ++ ".app.src"},
{"common/sys.config", Name ++ "/config/sys.config"},
{"common/rebar.config", Name ++ "/rebar.config"}]
++ templater_network(Flags).

templater_network(#{name := Name, network := true} = Flags) ->
[{"network/grisp.ini.mustache",
Name ++ "/grisp/grisp2/common/deploy/files/grisp.ini.mustache"}]
++ templater_wifi(Flags)
++ templater_grisp_io(Flags);
templater_network(_) ->
[].

templater_wifi(#{name := Name, wifi := true}) ->
[{"network/wpa_supplicant.conf",
Name ++ "/grisp/default/common/deploy/files/wpa_supplicant.conf"}];
templater_wifi(_) ->
[].

templater_grisp_io(#{name := Name, grisp_io := false}) ->
[{"network/erl_inetrc",
Name ++ "/grisp/default/common/deploy/files/erl_inetrc"}];
templater_grisp_io(_) ->
[].

maybe_write_file(In, Out, Params) ->
case filelib:is_regular(Out) of
false ->
filelib:ensure_dir(Out),
debug("Rendering template file: ~p", [In]),
FileContent = grisp_tools_template:render(In, Params),
debug("Writing output file: ~p", [Out]),
file:write_file(Out, FileContent);
true -> info("File already exists: ~p~n", [Out])
end.
Loading