Skip to content

Commit 8ff5fd1

Browse files
committed
Run embedded avm on Linux and macOS
Most platforms run embedded avm in the flash. Implement a similar mechanism on generic_unix platform, to enable an escriptize-like experience. Signed-off-by: Paul Guyot <[email protected]>
1 parent 1ffaf68 commit 8ff5fd1

File tree

8 files changed

+275
-79
lines changed

8 files changed

+275
-79
lines changed

libs/estdlib/src/init.erl

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,34 @@
3939
%% @doc Entry point.
4040
%% @end
4141
%%-----------------------------------------------------------------------------
42-
-spec boot([binary() | atom()]) -> any().
42+
-spec boot([binary() | atom() | string()]) -> any().
43+
boot([<<"-s">>, escript, <<"--">>, _Filename | Args]) ->
44+
% Escript mode: check for start beam and main/1 function before starting kernel
45+
case atomvm:get_start_beam(escript) of
46+
{ok, ModuleNameBinary} ->
47+
% Remove .beam suffix if present (check last 5 bytes)
48+
Size = byte_size(ModuleNameBinary),
49+
ModuleName = if
50+
Size > 5 ->
51+
case binary:part(ModuleNameBinary, Size - 5, 5) of
52+
<<".beam">> -> binary:part(ModuleNameBinary, 0, Size - 5);
53+
_ -> ModuleNameBinary
54+
end;
55+
true -> ModuleNameBinary
56+
end,
57+
Module = binary_to_atom(ModuleName),
58+
case erlang:function_exported(Module, main, 1) of
59+
true ->
60+
{ok, _KernelPid} = kernel:start(boot, []),
61+
Module:main(Args);
62+
false ->
63+
io:format("Function ~s:main/1 is not exported~n", [Module]),
64+
error
65+
end;
66+
_ ->
67+
io:format("start_beam not found~n"),
68+
error
69+
end;
4370
boot([<<"-s">>, StartupModule]) when is_atom(StartupModule) ->
4471
% Until we have boot scripts, we just start kernel application.
4572
{ok, _KernelPid} = kernel:start(boot, []),

src/libAtomVM/globalcontext.c

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "context.h"
3030
#include "defaultatoms.h"
3131
#include "erl_nif_priv.h"
32+
#include "interop.h"
3233
#include "list.h"
3334
#include "mailbox.h"
3435
#include "posix_nifs.h"
@@ -720,21 +721,62 @@ Module *globalcontext_get_module_by_index(GlobalContext *global, int index)
720721
return result;
721722
}
722723

723-
run_result_t globalcontext_run(GlobalContext *glb, Module *startup_module, FILE *out_f)
724+
run_result_t globalcontext_run(GlobalContext *glb, Module *startup_module, FILE *out_f, int argc, char **argv)
724725
{
725726
Context *ctx = context_new(glb);
726727
ctx->leader = 1;
727728
Module *init_module = globalcontext_get_module(glb, INIT_ATOM_INDEX);
728729
if (IS_NULL_PTR(init_module)) {
730+
if (IS_NULL_PTR(startup_module)) {
731+
fprintf(stderr, "Unable to locate entrypoint.\n");
732+
return RUN_NO_ENTRY_POINT;
733+
}
729734
context_execute_loop(ctx, startup_module, "start", 0);
730735
} else {
731-
if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(2) + LIST_SIZE(2, 0)) != MEMORY_GC_OK)) {
732-
fprintf(stderr, "Unable to allocate arguments.\n");
733-
return RUN_MEMORY_FAILURE;
736+
// Build boot arguments based on whether we're in embedded mode
737+
if (argc > 0 && argv != NULL) {
738+
// Embedded mode: ["-s", escript, "--" | argv_strings]
739+
// Calculate heap size needed
740+
size_t heap_needed = term_binary_heap_size(2) + // "-s"
741+
term_binary_heap_size(2) + // "--"
742+
LIST_SIZE(3, 0); // list for ["-s", escript, "--"]
743+
744+
// Calculate space for argv strings
745+
for (int i = 0; i < argc; i++) {
746+
size_t arg_len = strlen(argv[i]);
747+
heap_needed += CONS_SIZE * arg_len + LIST_SIZE(1, 0);
748+
}
749+
750+
if (UNLIKELY(memory_ensure_free(ctx, heap_needed) != MEMORY_GC_OK)) {
751+
fprintf(stderr, "Unable to allocate arguments.\n");
752+
return RUN_MEMORY_FAILURE;
753+
}
754+
755+
// Build the argv list in reverse: [argvn, ..., argv0, "--", escript, "-s"]
756+
term args_list = term_nil();
757+
for (int i = argc - 1; i >= 0; i--) {
758+
term arg = interop_chars_to_list(argv[i], strlen(argv[i]), &ctx->heap);
759+
args_list = term_list_prepend(arg, args_list, &ctx->heap);
760+
}
761+
762+
term separator = term_from_literal_binary("--", strlen("--"), &ctx->heap, glb);
763+
args_list = term_list_prepend(separator, args_list, &ctx->heap);
764+
765+
term escript_atom = globalcontext_make_atom(glb, ATOM_STR("\x7", "escript"));
766+
args_list = term_list_prepend(escript_atom, args_list, &ctx->heap);
767+
768+
term s_opt = term_from_literal_binary("-s", strlen("-s"), &ctx->heap, glb);
769+
ctx->x[0] = term_list_prepend(s_opt, args_list, &ctx->heap);
770+
} else {
771+
// Non-embedded mode: ["-s", startup_module]
772+
if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(2) + LIST_SIZE(2, 0)) != MEMORY_GC_OK)) {
773+
fprintf(stderr, "Unable to allocate arguments.\n");
774+
return RUN_MEMORY_FAILURE;
775+
}
776+
term s_opt = term_from_literal_binary("-s", strlen("-s"), &ctx->heap, glb);
777+
term list = term_list_prepend(module_get_name(startup_module), term_nil(), &ctx->heap);
778+
ctx->x[0] = term_list_prepend(s_opt, list, &ctx->heap);
734779
}
735-
term s_opt = term_from_literal_binary("-s", strlen("-s"), &ctx->heap, glb);
736-
term list = term_list_prepend(module_get_name(startup_module), term_nil(), &ctx->heap);
737-
ctx->x[0] = term_list_prepend(s_opt, list, &ctx->heap);
738780

739781
context_execute_loop(ctx, init_module, "boot", 1);
740782
}

src/libAtomVM/globalcontext.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ typedef enum run_result_t
9595
RUN_SUCCESS = 0,
9696
RUN_MEMORY_FAILURE = 1,
9797
RUN_RESULT_NOT_OK = 2,
98+
RUN_NO_ENTRY_POINT = 3,
9899
} run_result_t;
99100

100101
struct GlobalContext
@@ -530,9 +531,11 @@ Module *globalcontext_load_module_from_avm(GlobalContext *global, const char *mo
530531
* @param global the global context
531532
* @param start_module the start module
532533
* @param out_f file to print the result to, or NULL
534+
* @param argc number of command-line arguments (0 for non-embedded mode)
535+
* @param argv command-line arguments (NULL for non-embedded mode)
533536
* @returns RUN_SUCCESS or an error code
534537
*/
535-
run_result_t globalcontext_run(GlobalContext *global, Module *start_module, FILE *out_f);
538+
run_result_t globalcontext_run(GlobalContext *global, Module *start_module, FILE *out_f, int argc, char **argv);
536539

537540
#ifndef __cplusplus
538541
static inline uint64_t globalcontext_get_ref_ticks(GlobalContext *global)

src/platforms/emscripten/src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ static int start(void)
9696
return EXIT_FAILURE;
9797
}
9898

99-
run_result_t ret_value = globalcontext_run(global, main_module, stdout);
99+
run_result_t ret_value = globalcontext_run(global, main_module, stdout, 0, NULL);
100100

101101
int status;
102102
if (ret_value == RUN_SUCCESS) {

src/platforms/esp32/main/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void app_main()
123123
ESP_LOGI(TAG, "Starting %s...", startup_module_name);
124124
fprintf(stdout, "---\n");
125125

126-
run_result_t result = globalcontext_run(glb, mod, stdout);
126+
run_result_t result = globalcontext_run(glb, mod, stdout, 0, NULL);
127127

128128
bool reboot_on_not_ok =
129129
#if defined(CONFIG_REBOOT_ON_NOT_OK)

0 commit comments

Comments
 (0)