Skip to content

Commit 5258f57

Browse files
committed
Tighten up packbeam_api
This change set tightens up the packbeam API to expose just enough information about AVM files as is needed without exposing internal implementation details. Documentation has been written for the API, and we have moved to use `ex_doc` instead of `edoc` for documentation generation. Signed-off-by: Fred Dushin <[email protected]>
1 parent 1874698 commit 5258f57

File tree

9 files changed

+256
-114
lines changed

9 files changed

+256
-114
lines changed

.github/workflows/build-and-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: "ubuntu-20.04"
1414
strategy:
1515
matrix:
16-
otp: ["22", "23", "24"]
16+
otp: ["24", "25", "26"]
1717

1818
steps:
1919
# Setup

ChangeLog renamed to CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.7.1] (unreleased)
8+
9+
- Enhanced `packbeam_api` to make it more maintainable.
10+
- Changed documentation to use [`rebar3_ex_doc`](https://hexdocs.pm/rebar3_ex_doc/readme.html)
11+
712
## [0.7.0]
813

914
- Added `version` sub-command to print version to the console

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
## All rights reserved.
44
##
55

6-
all: compile escript edoc etest rel
6+
all: compile escript docs etest rel
77

88
compile:
99
rebar3 compile
1010

1111
escript:
1212
rebar3 escriptize
1313

14-
edoc:
15-
rebar3 edoc
14+
docs:
15+
rebar3 ex_doc
1616

1717
etest:
1818
rebar3 eunit --cover

README.md

Lines changed: 104 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
# `atomvvm_packbeam`
22

3-
An Erlang Escript and OTP library used to generate an <a href="http://github.com/bettio/AtomVM">AtomVM</a> AVM file from a set of files (beam files, previously built AVM files, or even arbitrary data files).
3+
An Erlang Escript and library used to generate an [AtomVM](http://github.com/atomvm/AtomVM) AVM file from a set of files (beam files, previously built AVM files, or even arbitrary data files).
44

55
This tool roughly approximates the functionality of the AtomVM `PackBEAM` utility, except:
66

77
* Support for multiple data types, include beam files, text files, etc
8-
* "Smart" extraction of beams from AVM files, so that only the beams that are needed are packed
8+
* "Pruned" extraction of beams from AVM files, so that only the beams that are needed are packed
9+
* Support for embedded OTP applications in your PackBEAM files.
910

10-
The `packbeam` tool may be used on its own. More typically, it is used internally as part of the <a href="https://github.com/fadushin/atomvm_rebar3_plugin">atomvm_rebar3_plugin</a> `rebar3` plugin.
11+
The `packbeam` tool may be used on its own as a stand-alone command-line utility. More typically, it is used internally as part of the [`atomvm_rebar3_plugin`](https://github.com/atomvm/atomvm_rebar3_plugin) [`rebar3`](https://rebar3.org) plugin.
1112

12-
# Prerequisites
13+
## Prerequisites
1314

14-
Building Packbeam requires Erlang/OTP 22 or later, for compatibility with AtomVM, as well as a local installation of `rebar3`. Optionally, any recent version of `make` may be used to simplify builds.
15+
Building `packbeam` requires a version of Erlang/OTP compatible with [AtomVM](https://github.com/atomvm/AtomVM), as well as a local installation of [`rebar3`](https://rebar3.org). Optionally, any recent version of `make` may be used to simplify builds. Consult the [AtomVM Documentation](https://www.atomvm.net/doc/master/) for information about supported OTP versions.
1516

16-
# Build
17+
## Build
1718

1819
To build a release, run the following commands:
1920

@@ -46,7 +47,7 @@ For example:
4647
packbeam <sub-command> <options> <args>
4748
...
4849

49-
# `packbeam` command
50+
## `packbeam` command
5051

5152
The `packbeam` command is used to create an AVM file from a list of beam and other file types, to list the contents of an AVM file, or to delete elements from an AVM file.
5253

@@ -106,7 +107,7 @@ The `packbeam` command will return an exit status of 0 on successful completion
106107

107108
The `packbeam` sub-commands are described in more detail below.
108109

109-
## `create` sub-command
110+
### `create` sub-command
110111

111112
To create an AVM file from a list of beam files, use the `create` sub-command to create an AVM file. The first argument is take to be the output AVM file, following by the files you would like to add, e.g.,
112113

@@ -122,7 +123,7 @@ The input files specified in the create subcommand may be among the following ty
122123

123124
Note that beam files specified are stripped of their path information, inside of the generated AVM file. Any files that have the same name will be added in the order they are listed on the command line. However, AtomVM will only resolve the first such file when loading modules at run-time.
124125

125-
### Start Entrypoint
126+
#### Start Entrypoint
126127

127128
If you are building an application that provides a start entrypoint (as opposed to a library, suitable for inclusion in another AVM file), then at least one beam module in an AVM file must contain a `start/0` entry-point, i.e., a function called `start` with arity 0. AtomVM will use this entry-point as the first function to execute, when starting.
128129

@@ -138,19 +139,19 @@ In addition, you may specify a "normal" (i.e., non-beam or non-AVM) file. Norma
138139

139140
> Note. It is conventional in AtomVM for normal files to have the path `<module-name>/priv/<file-name>`.
140141
141-
### Pruning
142+
#### Pruning
142143

143144
If you specify the `--prune` (alternatively, `-p`) flag, then `packbeam` will only include beam files that are transitively dependent on the entry-point beam. Transitive dependencies are determined by imports, as well as use of an atom in a module (e.g, as the result of a dynamic function call, based on a module name).
144145

145146
If there is no beam file with a `start/0` entry-point defined in the list of input modules and the `--prune` flag is used, the command will fail. You should _not_ use the `--prune` flag if you are trying to build libraries suitable for inclusion on other AtomVM applications.
146147

147-
### Line number information
148+
#### Line number information
148149

149150
By default, the `packbeam` tool will generate line number information for embedded BEAM files. Line number information is included in Erlang stacktraces, giving developers more clues into bugs in their programs. However, line number information does increase the size of AVM files, and in some cases can have an impact on memory in running applications.
150151

151152
For production applications that have no need for line number information, we recommend using the `-r` (or `--remove_lines`) flags, which will strip line number information from embedded BEAM files.
152153

153-
## `list` sub-command
154+
### `list` sub-command
154155

155156
The `list` sub-command will print the contents of an AVM file to the standard output stream.
156157

@@ -170,7 +171,7 @@ You may use the `--format` (alternatively, `-f`) option to specify an output for
170171
* `bare` Output just the module name, with no annotations.
171172
* `default` Output the module name, size (in brackets), and whether the file provides a `start/0` entrypoint, indicated by an asterisk (`*`). The `default` output is used if the `--format` option is not specified.
172173

173-
## `extract` sub-command
174+
### `extract` sub-command
174175

175176
The `extract` sub-command can be used to extract elements from an AVM file.
176177

@@ -190,7 +191,7 @@ For example:
190191
x mylib/priv/sample.txt
191192

192193

193-
## `delete` sub-command
194+
### `delete` sub-command
194195

195196
The `delete` sub-command can be used to remove elements from an AVM file.
196197

@@ -203,6 +204,93 @@ For example:
203204
mylib.beam * [284]
204205
mylib/priv/sample.txt [29]
205206

206-
# `packbeam_api` API
207+
## `packbeam_api` API
207208

208-
> TODO Used by rebar plugin
209+
In addition to being an `escript` command-line utility, this project provides an Erlang API and library for manipulating AVM files. Simply include `atomvm_packbeam` as a dependency in your `rebar.config`, and you will have access to this API.
210+
211+
> For more detailed information about this API, see the [`packbeam_api` Reference](packbeam_api.html).
212+
213+
### Creating PackBEAM files
214+
215+
To create a PackBEAM file, use the `packbeam_api:create/2` function. Specify the output path of the AVM you would like to create, followed by a list of paths to the files that will go into the AVM file. Typically, these paths are a list of BEAM files, though you can also include plain data files, in addition to previously created AVM files. Previously-created AVM files will be copied into the output AVM file.
216+
217+
> Note. Specify the file system paths to all files. BEAM file path information will be stripped from the AVM element path data. Any plain data files (non-BEAM files) will retain their path information. See the [AtomVM Documentation](https://www.atomvm.net/doc/master/) about how to create plain data files in AVM files that users can retrieved via the `atomvm:read_priv/2` function.
218+
219+
%% erlang
220+
ok = packbeam_api:create(
221+
"/path/to/output.avm", [
222+
"/path/to/foo.beam",
223+
"/path/to/bar.beam",
224+
"/path/to/myapp/priv/sample.txt",
225+
"/path/to/some_lib.avm"
226+
]
227+
).
228+
229+
Alternatively, you may specify a set of options with the `packbeam_api:create/3` function, which takes a map as the third parameter.
230+
231+
| Key | Type | Deafult | Description |
232+
|-----|------|---------|-------------|
233+
| `prune` | `boolean()` | `false` | Specify whether to prune the output AVM file. Pruned AVM files can take considerably less space and hence may lead to faster development times. |
234+
| `start` | `module()` | n/a | Specify the start module, if it can't be determined automatically from the application. |
235+
| `application` | `module()` | n/a | Specify the application module. The `<application>.app` file will be encoded and included as an element in the AVM file with the path `<module>/priv/application.bin` |
236+
| `include_lines` | `boolean()` | `true` | Specify whether to include line number information in generated AVM files. |
237+
238+
### Listing the contents of PackBEAM files
239+
240+
You can list the contents of PackBEAM files using the `packbeam_api:list/1` function. Specify the file system path to the PackBEAM file you would like to list:
241+
242+
%% erlang
243+
AVMElements = packbeam_api:list("/path/to/input.avm").
244+
245+
The returned `AVMElements` is list of an opaque data structures and should not be interpreted by user applications. However, several functions are exposed to retrieve information about elements in this list.
246+
247+
To get the element name, use the `packbeam_api:get_element_name/1` function, passing in an AVM element. The return type is a `string()` and represents the path in the AVM file for the AVM element.
248+
249+
%% erlang
250+
AVMElementName = packbeam_api:get_element_name(AVMElement).
251+
252+
To get the element data (as a binary) use the `packbeam_api:get_element_data/1` function, passing in an AVM element. The return type is a `binary()` containing the actual data in the AVM element.
253+
254+
%% erlang
255+
AVMElementData = packbeam_api:get_element_data(AVMElement).
256+
257+
To get the element module (as an atom) use the `packbeam_api:get_element_module/1` function, passing in an AVM element. The return type is a `module()` and the module name of the AVM element.
258+
259+
Note that if the AVM element is not a BEAM file, this function returns `undefined`.
260+
261+
%% erlang
262+
AVMElementModule = packbeam_api:get_element_module(AVMElement).
263+
264+
To determine if the element is a BEAM file, use the `packbeam_api:is_beam/1` function, passing in an AVM element. The return value is a `boolean()`.
265+
266+
%% erlang
267+
IsBEAM = packbeam_api:is_beam(AVMElement).
268+
269+
To determine if the element is an entrypoint BEAM (i.e., it exports a `start/0` function), use the `packbeam_api:is_entrypoint/1` function, passing in an AVM element. The return value is a `boolean()`.
270+
271+
%% erlang
272+
IsEntrypoint = packbeam_api:is_entrypoint(AVMElement).
273+
274+
### Deleting entries from PackBEAM files
275+
276+
You can delete entries from an AVM file using the `packbeam_api:delete/3` function. Specify the file system path to the PackBEAM file you would like to delete from, the output path you would like to write the new AVM file to, and a list of AVM elements you would like to delete:
277+
278+
%% erlang
279+
ok = packbeam_api:delete(
280+
"/path/to/input.avm",
281+
"/path/to/ouput.avm",
282+
["foo.beam", "myapp/priv/sample.txt"]
283+
).
284+
285+
> Note. You may specify the same values for the input and output paths. In this case, the input AVM file will be _over-written_ by the new AVM file.
286+
287+
### Extracting entries from PackBEAM files
288+
289+
You can extract elements from an AVM file using the `packbeam_api:extract/3` function. Specify the file system path to the PackBEAM file you would like to extract from, a list of AVM elements you would like to extract, and the output directory into which would like to extract the files:
290+
291+
%% erlang
292+
ok = packbeam_api:extract(
293+
"/path/to/input.avm",
294+
["foo.beam", "myapp/priv/sample.txt"],
295+
"/tmp"
296+
).

rebar.config

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
%% Copyright (c) dushin.net
33
%% All rights reserved.
44
%%
5-
{erl_opts, [no_debug_info]}.
5+
{erl_opts, [debug_info]}.
66
{deps, []}.
77

88
{escript_incl_apps, [atomvm_packbeam]}.
@@ -12,11 +12,20 @@
1212

1313
{ex_doc, [
1414
{source_url, <<"https://github.com/atomvm/atomvm_packbeam">>},
15-
{extras, [<<"README.md">>, <<"LICENSE">>]},
16-
{main, <<"readme">>}
15+
{extras, [
16+
<<"README.md">>,
17+
<<"CHANGELOG.md">>,
18+
<<"UPDATING.md">>,
19+
<<"LICENSE">>,
20+
<<"CONTRIBUTING.md">>,
21+
<<"CODE_OF_CONDUCT.md">>
22+
]},
23+
{main, <<"README.md">>},
24+
{api_reference, true},
25+
{skip_undefined_reference_warnings_on, ["README.md"]}
1726
]}.
18-
{hex, [{doc, edoc}]}.
19-
{plugins, [rebar3_hex, rebar3_proper]}.
27+
{hex, [{doc, #{provider => ex_doc}}]}.
28+
{plugins, [rebar3_hex, rebar3_proper, rebar3_ex_doc]}.
2029

2130
%% Profiles
2231
{profiles, [

src/packbeam.erl

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -272,35 +272,34 @@ print_modules(Modules, Format) ->
272272
print_module(ParsedFile, undefined) ->
273273
print_module(ParsedFile, "default");
274274
print_module(ParsedFile, "default") ->
275-
ModuleName = proplists:get_value(module_name, ParsedFile),
276-
Flags = proplists:get_value(flags, ParsedFile),
277-
Data = proplists:get_value(data, ParsedFile),
275+
Name = packbeam_api:get_element_name(ParsedFile),
276+
Data = packbeam_api:get_element_data(ParsedFile),
278277
io:format(
279278
"~s~s [~p]~n", [
280-
ModuleName,
281-
case packbeam_api:is_entrypoint(Flags) of
279+
Name,
280+
case packbeam_api:is_entrypoint(ParsedFile) of
282281
true -> " *";
283282
_ -> ""
284283
end,
285284
byte_size(Data)
286285
]
287286
);
288287
print_module(ParsedFile, "csv") ->
289-
ModuleName = proplists:get_value(module_name, ParsedFile),
290-
Data = proplists:get_value(data, ParsedFile),
288+
Name = packbeam_api:get_element_name(ParsedFile),
289+
Data = packbeam_api:get_element_data(ParsedFile),
291290
io:format(
292291
"~s,~p,~p,~p~n", [
293-
ModuleName,
292+
Name,
294293
packbeam_api:is_beam(ParsedFile),
295294
packbeam_api:is_entrypoint(ParsedFile),
296295
byte_size(Data)
297296
]
298297
);
299298
print_module(ParsedFile, "bare") ->
300-
ModuleName = proplists:get_value(module_name, ParsedFile),
299+
Name = packbeam_api:get_element_name(ParsedFile),
301300
io:format(
302301
"~s~n", [
303-
ModuleName
302+
Name
304303
]
305304
);
306305
print_module(_ParsedFile, Format) ->

0 commit comments

Comments
 (0)