Skip to content

Commit d46605d

Browse files
committed
Add Elixir interface to APIs
Adds an Elixir wrapper providing a native interface for the Erlang packbeam_api module. Signed-off-by: Winford <[email protected]>
1 parent 4de8162 commit d46605d

File tree

11 files changed

+594
-0
lines changed

11 files changed

+594
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#
2+
# Copyright 2022 Fred Dushin <[email protected]>
3+
#
4+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
5+
#
6+
7+
name: Build and Test Elixir PackBEAM.API
8+
9+
on:
10+
push:
11+
paths:
12+
- "mix.exs"
13+
- "lib/**/*.exs"
14+
- "lib/**/*.ex"
15+
- "test/**/*.exs"
16+
- "test/**/*.ex"
17+
pull_request:
18+
paths:
19+
- "mix.exs"
20+
- "lib/**/*.ex"
21+
- "lib/**/*.exs"
22+
- "test/**/*.exs"
23+
- "test/**/*.ex"
24+
workflow_dispatch:
25+
26+
env:
27+
ELIXIR_ASSERT_TIMEOUT: 2000
28+
ELIXIRC_OPTS: "--warnings-as-errors"
29+
LANG: C.UTF-8
30+
31+
permissions:
32+
contents: read
33+
34+
jobs:
35+
build-and-test-elixir:
36+
runs-on: "ubuntu-24.04"
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
# otp: ["25", "26", "27", "28"]
41+
include:
42+
- otp: "25"
43+
elixir_version: "1.14"
44+
- otp: "26"
45+
elixir_version: "1.17"
46+
- otp: "27"
47+
elixir_version: "1.18"
48+
- otp: "28"
49+
elixir_version: "1.19"
50+
51+
steps:
52+
# Setup
53+
- name: "Checkout repo"
54+
uses: actions/checkout@v2
55+
with:
56+
submodules: 'recursive'
57+
58+
- uses: erlef/setup-beam@v1
59+
with:
60+
otp-version: ${{ matrix.otp }}
61+
elixir-version: ${{ matrix.elixir_version }}
62+
63+
# Builder info
64+
- name: "System info"
65+
run: |
66+
echo "**uname:**"
67+
uname -a
68+
echo "**OTP version:**"
69+
cat $(dirname $(which erlc))/../releases/RELEASES || true
70+
echo "**Elixir version:**"
71+
elixir --version
72+
73+
# Build
74+
- name: "Compile with mix"
75+
run: mix compile
76+
77+
# Test
78+
- name: "Run tests with mix"
79+
run: mix test

lib/packbeam.ex

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#
2+
# This file is part of atomvm_packbeam.
3+
#
4+
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
#
20+
21+
defmodule PackBEAM.API do
22+
@moduledoc """
23+
A library used to generate an
24+
<a href="http://github.com/atomvm/AtomVM">AtomVM</a> AVM file from a set of
25+
files (beam files, previously built AVM files, or even arbitrary data files).
26+
27+
This is an Elixir interface for the native Erlang packbeam_api library.
28+
"""
29+
30+
@type path :: path
31+
@type avm_element :: [atom | {atom, term}]
32+
@type avm_element_name :: String.t()
33+
@type options :: %{
34+
prune: prune :: boolean,
35+
start_module: start :: atom,
36+
include_lines: lines :: boolean,
37+
arch: arch :: atom,
38+
platform: platform :: atom
39+
}
40+
41+
@doc """
42+
Create an AVM file with default options.
43+
44+
Equivalent to `create(outpath, inputpaths, defaultopts)`
45+
46+
where `defaultopts` is `#%{
47+
:prune => false,
48+
:start_module => :undefined,
49+
:application_module => :undefined,
50+
:include_lines => false,
51+
:arch => :undefined,
52+
:platform => :generic_unix
53+
}`
54+
"""
55+
@spec create(outpath :: path, inputpaths :: [path]) :: :ok | {:error, reason :: term}
56+
def create(outpath, inputpaths) do
57+
:packbeam_api.create(String.to_charlist(outpath), String.to_charlist(inputpaths))
58+
end
59+
60+
@doc """
61+
Create an AVM file.
62+
63+
This function will create an AVM file at the location specified in `outpath`, using the input
64+
files specified in `inputpaths` using the specified `options`.
65+
"""
66+
@spec create(outpath :: path, inputpaths :: [path], options :: options) ::
67+
:ok | {:error, reason :: term}
68+
def create(outpath, inputpaths, options) do
69+
:packbeam_api.create(String.to_charlist(outpath), inputpaths, options)
70+
end
71+
72+
@doc """
73+
List the contents of an AVM.
74+
"""
75+
@spec list(inputpath :: path) :: [avm_element]
76+
def list(inputpath) do
77+
:packbeam_api.list(String.to_charlist(inputpath))
78+
end
79+
80+
@doc """
81+
Extract all or selected elements from an AVM file.
82+
83+
This function will extract elements of an AVM file at the location specified in `inputpath`,
84+
specified by the supplied list of names. The elements from the input AVM file will be written
85+
into the specified output directory, creating any subdirectories if the AVM file elements contain
86+
path information.
87+
"""
88+
@spec extract(inputpath :: path, amvelements :: [avm_element_name], outdir :: path) ::
89+
:ok | {:error, reason :: term}
90+
def extract(inputpath, amvelements, outdir) do
91+
elements = List.foldl(amvelements, [], fn x, acc -> [String.to_charlist(x) | acc] end)
92+
:packbeam_api.extract(inputpath, elements, outdir)
93+
end
94+
95+
@doc """
96+
Delete selected elements of an AVM file.
97+
98+
This function will delete elements of an AVM file at the location specified in `inputpath`,
99+
specified by the supplied list of names. The output AVM file is written to `outpath`, which may
100+
be the same as `inputpath`.
101+
"""
102+
@spec delete(outpath :: path, inputpath :: path, amvelements :: [avm_element_name]) ::
103+
:ok | {:error, reason :: term}
104+
def delete(outpath, inputpath, amvelements) do
105+
elements = List.foldl(amvelements, [], fn x, acc -> [String.to_charlist(x) | acc] end)
106+
:packbeam_api.delete(String.to_charlist(outpath), inputpath, String.to_charlist(elements))
107+
end
108+
109+
@doc """
110+
Return the name of the element.
111+
"""
112+
@spec get_element_name(amvelement :: avm_element) :: atom
113+
def get_element_name(amvelement) do
114+
:packbeam_api.get_element_name(String.to_charlist(amvelement))
115+
end
116+
117+
@doc """
118+
Return the AVM element data.
119+
"""
120+
@spec get_element_data(amvelement :: avm_element) :: binary
121+
def get_element_data(amvelement) do
122+
:packbeam_api.get_element_data(String.to_charlist(amvelement))
123+
end
124+
125+
@doc """
126+
Return AVM element module, if the element is a BEAM file.
127+
"""
128+
@spec get_element_module(amvelement :: avm_element) :: module | :undefined
129+
def get_element_module(amvelement) do
130+
:packbeam_api.get_element_module(String.to_charlist(amvelement))
131+
end
132+
133+
@doc """
134+
Indicates whether the AVM file element is an entrypoint.
135+
"""
136+
@spec is_entrypoint(amvelement :: avm_element) :: boolean
137+
def is_entrypoint(amvelement) do
138+
:packbeam_api.is_entrypoint(String.to_charlist(amvelement))
139+
end
140+
141+
@doc """
142+
Indicates whether the AVM file element is a BEAM file.
143+
"""
144+
@spec is_beam(amvelement :: avm_element) :: boolean
145+
def is_beam(amvelement) do
146+
:packbeam_api.is_beam(String.to_charlist(amvelement))
147+
end
148+
end

mix.exs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#
2+
# This file is part of atomvm_packbeam.
3+
#
4+
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
#
20+
21+
defmodule Packbeam.MixProject do
22+
use Mix.Project
23+
24+
def project do
25+
[
26+
app: :packbeam,
27+
version: "0.7.5",
28+
elixir: "~> 1.7",
29+
start_permanent: Mix.env() == :prod,
30+
deps: deps(),
31+
32+
# Docs
33+
name: "Packbeam",
34+
source_url: "https://github.com/atomvm/atomvm_packbeam",
35+
homepage_url: "https://www.atomvm.org/",
36+
docs: [
37+
# The main page in the docs
38+
main: "README.md",
39+
skip_undefined_reference_warnings_on: "README.md",
40+
api_reference: true,
41+
output: "elixir_docs",
42+
extras: [
43+
"README.md",
44+
"CHANGELOG.md",
45+
"UPDATING.md",
46+
"LICENSE",
47+
"CONTRIBUTING.md",
48+
"CODE_OF_CONDUCT.md"
49+
]
50+
],
51+
52+
# Tests
53+
elixirc_paths: elixirc_paths(Mix.env())
54+
]
55+
end
56+
57+
defp elixirc_paths(:test), do: ["lib", "test"]
58+
defp elixirc_paths(_), do: ["lib"]
59+
60+
# Run "mix help compile.app" to learn about applications.
61+
def application do
62+
[
63+
extra_applications: [:logger]
64+
]
65+
end
66+
67+
# Run "mix help deps" to learn about dependencies.
68+
defp deps do
69+
[
70+
# TODO: add property tests
71+
# {:propcheck, "~> 1.4", only: [:test, :dev]}
72+
]
73+
end
74+
end

test/a.ex

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#
2+
# This file is part of atomvm_packbeam.
3+
#
4+
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
#
20+
21+
defmodule A do
22+
@moduledoc """
23+
Test module for TestPackbeam
24+
"""
25+
26+
def start do
27+
B.start()
28+
end
29+
end

test/b.ex

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# This file is part of atomvm_packbeam.
3+
#
4+
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
#
20+
21+
defmodule B do
22+
@moduledoc """
23+
Test module for TestPackbeam
24+
"""
25+
26+
def start do
27+
E.b_calls_me()
28+
module = get_module()
29+
module.test()
30+
end
31+
32+
defp get_module() do
33+
C
34+
end
35+
end

0 commit comments

Comments
 (0)