Skip to content

Commit

Permalink
Merge pull request #20 from atomvm/add-bootstrap-task
Browse files Browse the repository at this point in the history
Add support for the bootstrap task.
  • Loading branch information
UncleGrumpy authored Nov 16, 2023
2 parents ca497eb + c590bc9 commit 3c45d6b
Show file tree
Hide file tree
Showing 12 changed files with 477 additions and 13 deletions.
28 changes: 16 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.2]
## [0.7.3] (unreleased)

- Added support for compiling "bootstrap" erlang files that `rebar3` otherwise cannot compile.

## [0.7.2] (2023.10.24)

- Updated to depend on `atomvm_packbeam` version `0.7.1`, to make use of `packbeam_api` changes.
- Added tests for `packbeam`, `esp32`, and `stm32` tasks.
- Generate `ex_doc` documentation instead of `edoc`.
- Added `version` task to print the version of the plugin to the console

## [0.7.1]
## [0.7.1] (2023.10.18)

- Fixed a bug whereby a missing `atomvm_rebar3_plugin` entry in `rebar.config` would crash the `packbeam` task.

## [0.7.0]
## [0.7.0] (2023.10.18)

- Moved atomvm tasks under the `atomvm` namespace (with support for deprecated tasks in the default namespace)
- Added `utf2create` and `pico_flash` tasks, for Raspberry Pico support
- Added support for setting options in `rebar.config`
- Added `--list` (`-l`) option to `packbeam` to to display contents of generated AVM files.

## [0.6.1]
## [0.6.1] (2023.07.16)

### Added

Expand All @@ -34,30 +38,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated dependency on `atomvm_packbeam` 0.6 or later
- Changed default to not remove lines from generated AVM files

## [0.6.0]
## [0.6.0] (2022.12.18)

### Added
- Added ability to include `<<"Line">>` chunks in BEAM files in generated AVM files

### Changed
- Updated dependency on `atomvm_packbeam` 0.6.0

## [0.5.1]
## [0.5.1] (2022.08.31)

### Fixed
- Fixed Hex dependency on atomvm_packbeam 0.5.0

## [0.5.0]
## [0.5.0] (2022.08.28)

### Added
- Added packing of application bin file to packbeam file.

## [0.4.1]
## [0.4.1] (2022.06.19)

### Changed
- Updated dependency on `atomvm_packbeam` 0.4.1

## [0.4.0]
## [0.4.0] (2022.05.21)

### Added
- Added `erlfmt` plugin and formatted code.
Expand All @@ -66,12 +70,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fixed a bug that prevented files in directories inside of the `priv` directory to be included in packbeam files.

## [0.3.0]
## [0.3.0] (2022.05.18)

### Changed
- Updated dependency on `atomvm_packbeam` `0.3.0`

## [0.2.0]
## [0.2.0] (?)

### Added
- Added plugin template for generating applications
Expand All @@ -81,5 +85,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Updated default flash location

## [0.1.0]
## [0.1.0] (2020.05.17)
- Initial Release
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ The [`rebar3`](https://rebar3.org) plugin provides the following tasks under the
* `uf2create` Generate a u2f binary from an AtomVM packbeam file.
* `pico_flash` Flash "packed" uf2 files to RP2040 (RPi Pico) devices by copying to FATfs.
* `version` Print the version of the [`atomvm_rebar3_plugin`](https://atomvm.github.io/atomvm_rebar3_plugin) to the console.
* `bootstrap` Compile Erlang files that `rebar3` otherwise cannot compile. Typically, such files include modules from the OTP `kernel` or `stdlib` application that `rebar3` uses internally for its own implementation.

> IMPORTANT! Some of the above tasks were previously located under the default [`rebar3`](https://rebar3.org) namespace; however, the commands under the default namespace have been DEPRECATED. Users will get a warning message on the console when using deprecated tasks, and any deprecated tasks may be removed in the future without warning. Be sure to migrate any scripts or code you have to use the `atomvm` namespace.
Expand Down Expand Up @@ -485,6 +486,59 @@ Any setting specified on the command line take precedence over entries in `rebar

The `uf2create` task depends on the `packbeam` task, so the packbeam file will get automatically built if any changes have been made to its dependencies.

### The `version` task

use the `version` task to print the current verison of the [`atomvm_rebar3_plugin`](https://atomvm.github.io/atomvm_rebar3_plugin) to the console.

shell$ rebar3 atomvm version
0.7.2

### The `bootstrap` task

Use the `bootstrap` task to compile Erlang files that `rebar3` is otherwise unable to compile. Typically, such files include modules from the OTP `kernel` or `stdlib` application that `rebar3` uses internally for its own implementation.

> Note. The default `rebar3` `compile` task has the unfortunate feature (bug?) that it will load files that it compiles, which can be problematic if you are compiling modules that share names with modules that `rebar3` is using as part of its own implementation.
shell$ rebar3 help atomvm bootstrap

This plugin is used internally by the atomvm packbeam task to compile
modules that cannot be compiled directly by rebar.

Users typically have no reason to use this task directly.

Usage: rebar3 atomvm tstrap [-b <bootstrap_dir>] [-f <force>]

-b, --bootstrap_dir Bootstrap directory
-f, --force Force rebuild

To use this task, place files that you would like to have compiled by the task in the `bootstrap` directory of your rebar3 project (or in a directory of your choosing -- see below).

shell$ ls bootstrap
application.erl

Files in this directory will be compiled and included in any generated PackBEAM files.

> Note. The `bootstrap` task is used internally by the [`atomvm_rebar3_plugin`](https://atomvm.github.io/atomvm_rebar3_plugin) when the `packbeam` task is run. Users typically do not have a need to run this task manually.
The following table enumerates the properties that may be defined in your project's `rebar.config` file for this task. Use `bootstrap` as the key for any properties defined for this task.

| Key | Type | Description |
|-----|------|-------------|
| `force` | `boolean()` | Always force recompilation of bootstrap files, even if up to date |
| `bootstrap_dir` | `string() \| undefined` | (Optional) path to a directory containing bootstrap files. By default, the `bootstrap` task will use the `bootstrap` directory in the top-level project directory. The path may be relative (to where the command is run) or absolute. |

Example:

{atomvm_rebar3_plugin, [
{bootstrap, [
{bootstrap_dir, "/path/to/bootstrap_dir"}, force
]}
]}.

Any setting specified on the command line take precedence over entries in `rebar.config`, which in turn take precedence over the default values specified above.



## AtomVM App Template

The `atomvm_rebar3_plugin` contains template definitions for generating skeletal `rebar3` projects.
Expand Down
184 changes: 184 additions & 0 deletions src/atomvm_bootstrap_provider.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
%%
%% Copyright (c) 2023 Fred Dushin <fred@dushin.net>
%% All rights reserved.
%%
%% Licensed 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.
%%
-module(atomvm_bootstrap_provider).

-behaviour(provider).

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

-define(PROVIDER, bootstrap).
-define(DEPS, [{default, compile}]).
-define(OPTS, [
{bootstrap_dir, $b, "bootstrap_dir", boolean, "Bootstrap directory"},
{force, $f, "force", boolean, "Force rebuild"}
]).

-define(DEFAULT_OPTS, #{
bootstrap_dir => undefined,
force => false
}).

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

%%
%% provider implementation
%%

%% @hidden
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Provider = providers:create([
% The atomvm namespace
{namespace, atomvm},
% The 'user friendly' name of the task
{name, ?PROVIDER},
% The module implementation of the task
{module, ?MODULE},
% The task can be run by the user, always true
{bare, true},
% The list of dependencies
{deps, ?DEPS},
% How to use the plugin
{example, "rebar3 atomvm bootstrap"},
% list of options understood by the plugin
{opts, ?OPTS},
{short_desc, "Compiles bootstrap .erl files"},
{desc,
"~n"
"This plugin is used internally by the atomvm packbeam task to compile~n"
"modules that cannot be compiled directly by rebar.~n"
"~n"
"Users typically have no reason to use this task directly.~n"
}
]),
{ok, rebar_state:add_provider(State, Provider)}.

%% @hidden
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
try
Opts = get_opts(State),
rebar_api:debug("Effective opts for ~p: ~p", [?PROVIDER, Opts]),
ok = do_bootstrap(
rebar_state:project_apps(State),
maps:get(bootstrap_dir, Opts),
maps:get(force, Opts)
),
{ok, State}
catch
C:E:S ->
rebar_api:error("An error occurred in the ~p task. Class=~p Error=~p Stacktrace=~p~n", [?PROVIDER, C, E, S]),
{error, E}
end.

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

%%
%% internal functions
%%

%% @private
get_opts(State) ->
{ParsedArgs, _} = rebar_state:command_parsed_args(State),
RebarOpts = atomvm_rebar3_plugin:get_atomvm_rebar_provider_config(State, ?PROVIDER),
ParsedOpts = atomvm_rebar3_plugin:proplist_to_map(ParsedArgs),
maps:merge(
?DEFAULT_OPTS,
maps:merge(RebarOpts, ParsedOpts)
).

%% @private
do_bootstrap(ProjectApps, BootstrapDir, Force) ->
lists:foreach(
fun(ProjectApp) ->
do_bootstrap_app(ProjectApp, BootstrapDir, Force)
end,
ProjectApps
).

%% @private
do_bootstrap_app(App, undefined, Force) ->
Dir = rebar_app_info:dir(App),
do_bootstrap_app(App, filename:join(Dir, "bootstrap"), Force);
do_bootstrap_app(App, BootstrapDir, Force) ->
% Dir = rebar_app_info:dir(App),
case filelib:is_dir(BootstrapDir) of
true ->
BootstrapFiles = [filename:join(BootstrapDir, Mod) || Mod <- filelib:wildcard("*.erl", BootstrapDir)],
lists:foreach(
fun(BootstrapFile) ->
do_bootstrap_file(App, BootstrapFile, Force)
end,
BootstrapFiles
);
_ ->
rebar_api:debug("No bootstrap dir ~s. Skipping...", [BootstrapDir]),
ok
end.

%% @private
do_bootstrap_file(App, BootstrapFile, Force) ->
OutDir = rebar_app_info:out_dir(App),
EBinDir = filename:join(OutDir, "ebin"),
maybe_compile(App, BootstrapFile, EBinDir, Force).

%% @private
maybe_compile(App, BootstrapFile, EBinDir, Force) ->
Basename = filename:basename(BootstrapFile, ".erl"),
BeamFile = filename:join(EBinDir, Basename ++ ".beam"),
case Force orelse needs_build(BootstrapFile, BeamFile) of
true ->
CompilerOptions = [{outdir, EBinDir}, report] ++
get_compiler_options(App),
rebar_api:debug("Compiling bootstrap file ~s with options ~p ...", [BootstrapFile, CompilerOptions]),
case compile:file(
BootstrapFile, CompilerOptions
) of
{ok, ModuleName} ->
rebar_api:info("Compiled bootstrap module ~p", [ModuleName]),
ok;
Error ->
rebar_api:error("Failed to compile bootstrap file ~s: ~p", [BootstrapFile, Error]),
Error
end;
_ ->
rebar_api:debug("bootstrap file ~s not in need of rebuild", [BootstrapFile]),
ok
end.

%% @private
get_compiler_options(App) ->
Opts = rebar_app_info:opts(App),
case dict:find(erl_opts, Opts) of
{ok, ErlOpts} ->
ErlOpts;
_ ->
[]
end.

%% @private
needs_build(Path, BeamFile) ->
not filelib:is_file(BeamFile) orelse
modified_time(Path) > modified_time(BeamFile).

%% @private
modified_time(Path) ->
{ok, #file_info{mtime = MTime}} = file:read_file_info(Path, [{time, posix}]),
MTime.
2 changes: 1 addition & 1 deletion src/atomvm_packbeam_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
-include_lib("kernel/include/file.hrl").

-define(PROVIDER, packbeam).
-define(DEPS, [{default, compile}]).
-define(DEPS, [bootstrap]).
-define(OPTS, [
{external, $e, "external", string, "External AVM modules"},
{force, $f, "force", boolean, "Force rebuild"},
Expand Down
1 change: 1 addition & 0 deletions src/atomvm_rebar3_plugin.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
-export([proplist_to_map/1, get_atomvm_rebar_provider_config/2]).

-define(PROVIDERS, [
atomvm_bootstrap_provider,
atomvm_packbeam_provider,
atomvm_esp32_flash_provider,
atomvm_pico_flash_provider,
Expand Down
Loading

0 comments on commit 3c45d6b

Please sign in to comment.