Skip to content
Draft
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
79 changes: 79 additions & 0 deletions .github/workflows/build-and-test-elixir.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#
# Copyright 2022 Fred Dushin <[email protected]>
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

name: Build and Test Elixir PackBEAM.API

on:
push:
paths:
- "mix.exs"
- "lib/**/*.exs"
- "lib/**/*.ex"
- "test/**/*.exs"
- "test/**/*.ex"
pull_request:
paths:
- "mix.exs"
- "lib/**/*.ex"
- "lib/**/*.exs"
- "test/**/*.exs"
- "test/**/*.ex"
workflow_dispatch:

env:
ELIXIR_ASSERT_TIMEOUT: 2000
ELIXIRC_OPTS: "--warnings-as-errors"
LANG: C.UTF-8

permissions:
contents: read

jobs:
build-and-test-elixir:
runs-on: "ubuntu-24.04"
strategy:
fail-fast: false
matrix:
# otp: ["25", "26", "27", "28"]
include:
- otp: "25"
elixir_version: "1.14"
- otp: "26"
elixir_version: "1.17"
- otp: "27"
elixir_version: "1.18"
- otp: "28"
elixir_version: "1.19"

steps:
# Setup
- name: "Checkout repo"
uses: actions/checkout@v2
with:
submodules: 'recursive'

- uses: erlef/setup-beam@v1
with:
otp-version: ${{ matrix.otp }}
elixir-version: ${{ matrix.elixir_version }}

# Builder info
- name: "System info"
run: |
echo "**uname:**"
uname -a
echo "**OTP version:**"
cat $(dirname $(which erlc))/../releases/RELEASES || true
echo "**Elixir version:**"
elixir --version

# Build
- name: "Compile with mix"
run: mix compile

# Test
- name: "Run tests with mix"
run: mix test
148 changes: 148 additions & 0 deletions lib/packbeam.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#
# This file is part of atomvm_packbeam.
#
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

defmodule PackBEAM.API do
@moduledoc """
A library used to generate an
<a href="http://github.com/atomvm/AtomVM">AtomVM</a> AVM file from a set of
files (beam files, previously built AVM files, or even arbitrary data files).

This is an Elixir interface for the native Erlang packbeam_api library.
"""

@type path :: path
@type avm_element :: [atom | {atom, term}]
@type avm_element_name :: String.t()
@type options :: %{
prune: prune :: boolean,
start_module: start :: atom,
include_lines: lines :: boolean,
arch: arch :: atom,
platform: platform :: atom
}

@doc """
Create an AVM file with default options.

Equivalent to `create(outpath, inputpaths, defaultopts)`

where `defaultopts` is `#%{
:prune => false,
:start_module => :undefined,
:application_module => :undefined,
:include_lines => false,
:arch => :undefined,
:platform => :generic_unix
}`
"""
@spec create(outpath :: path, inputpaths :: [path]) :: :ok | {:error, reason :: term}
def create(outpath, inputpaths) do
:packbeam_api.create(String.to_charlist(outpath), String.to_charlist(inputpaths))
end

@doc """
Create an AVM file.

This function will create an AVM file at the location specified in `outpath`, using the input
files specified in `inputpaths` using the specified `options`.
"""
@spec create(outpath :: path, inputpaths :: [path], options :: options) ::
:ok | {:error, reason :: term}
def create(outpath, inputpaths, options) do
:packbeam_api.create(String.to_charlist(outpath), inputpaths, options)
end

@doc """
List the contents of an AVM.
"""
@spec list(inputpath :: path) :: [avm_element]
def list(inputpath) do
:packbeam_api.list(String.to_charlist(inputpath))
end

@doc """
Extract all or selected elements from an AVM file.

This function will extract elements of an AVM file at the location specified in `inputpath`,
specified by the supplied list of names. The elements from the input AVM file will be written
into the specified output directory, creating any subdirectories if the AVM file elements contain
path information.
"""
@spec extract(inputpath :: path, amvelements :: [avm_element_name], outdir :: path) ::
:ok | {:error, reason :: term}
def extract(inputpath, amvelements, outdir) do
elements = List.foldl(amvelements, [], fn x, acc -> [String.to_charlist(x) | acc] end)
:packbeam_api.extract(inputpath, elements, outdir)
end

@doc """
Delete selected elements of an AVM file.

This function will delete elements of an AVM file at the location specified in `inputpath`,
specified by the supplied list of names. The output AVM file is written to `outpath`, which may
be the same as `inputpath`.
"""
@spec delete(outpath :: path, inputpath :: path, amvelements :: [avm_element_name]) ::
:ok | {:error, reason :: term}
def delete(outpath, inputpath, amvelements) do
elements = List.foldl(amvelements, [], fn x, acc -> [String.to_charlist(x) | acc] end)
:packbeam_api.delete(String.to_charlist(outpath), inputpath, String.to_charlist(elements))
end

@doc """
Return the name of the element.
"""
@spec get_element_name(amvelement :: avm_element) :: atom
def get_element_name(amvelement) do
:packbeam_api.get_element_name(String.to_charlist(amvelement))
end

@doc """
Return the AVM element data.
"""
@spec get_element_data(amvelement :: avm_element) :: binary
def get_element_data(amvelement) do
:packbeam_api.get_element_data(String.to_charlist(amvelement))
end

@doc """
Return AVM element module, if the element is a BEAM file.
"""
@spec get_element_module(amvelement :: avm_element) :: module | :undefined
def get_element_module(amvelement) do
:packbeam_api.get_element_module(String.to_charlist(amvelement))
end

@doc """
Indicates whether the AVM file element is an entrypoint.
"""
@spec is_entrypoint(amvelement :: avm_element) :: boolean
def is_entrypoint(amvelement) do
:packbeam_api.is_entrypoint(String.to_charlist(amvelement))
end

@doc """
Indicates whether the AVM file element is a BEAM file.
"""
@spec is_beam(amvelement :: avm_element) :: boolean
def is_beam(amvelement) do
:packbeam_api.is_beam(String.to_charlist(amvelement))
end
end
74 changes: 74 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#
# This file is part of atomvm_packbeam.
#
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

defmodule Packbeam.MixProject do
use Mix.Project

def project do
[
app: :packbeam,
version: "0.7.5",
elixir: "~> 1.7",
start_permanent: Mix.env() == :prod,
deps: deps(),

# Docs
name: "Packbeam",
source_url: "https://github.com/atomvm/atomvm_packbeam",
homepage_url: "https://www.atomvm.org/",
docs: [
# The main page in the docs
main: "README.md",
skip_undefined_reference_warnings_on: "README.md",
api_reference: true,
output: "elixir_docs",
extras: [
"README.md",
"CHANGELOG.md",
"UPDATING.md",
"LICENSE",
"CONTRIBUTING.md",
"CODE_OF_CONDUCT.md"
]
],

# Tests
elixirc_paths: elixirc_paths(Mix.env())
]
end

defp elixirc_paths(:test), do: ["lib", "test"]
defp elixirc_paths(_), do: ["lib"]

# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
# TODO: add property tests
# {:propcheck, "~> 1.4", only: [:test, :dev]}
]
end
end
29 changes: 29 additions & 0 deletions test/a.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# This file is part of atomvm_packbeam.
#
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

defmodule A do
@moduledoc """
Test module for TestPackbeam
"""

def start do
B.start()
end
end
35 changes: 35 additions & 0 deletions test/b.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#
# This file is part of atomvm_packbeam.
#
# Copyright 2025 Winford (Uncle Grumpy) <[email protected]>
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
#

defmodule B do
@moduledoc """
Test module for TestPackbeam
"""

def start do
E.b_calls_me()
module = get_module()
module.test()
end

defp get_module() do
C
end
end
Loading