From 20de0ab62450b6eeafc6e19a45534863fc4ad85d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Sat, 27 Dec 2025 18:54:12 +0100 Subject: [PATCH 01/15] building from source task --- lib/mix/tasks/esp32.build.ex | 303 +++++++++++++++++++++++++++++ lib/mix/tasks/esp32.build.flash.ex | 147 ++++++++++++++ 2 files changed, 450 insertions(+) create mode 100644 lib/mix/tasks/esp32.build.ex create mode 100644 lib/mix/tasks/esp32.build.flash.ex diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex new file mode 100644 index 0000000..fde90e4 --- /dev/null +++ b/lib/mix/tasks/esp32.build.ex @@ -0,0 +1,303 @@ +defmodule Mix.Tasks.Atomvm.Esp32.Build do + @moduledoc """ + Mix task for building AtomVM for ESP32 from source. + + Builds AtomVM from a local repository or git URL using ESP-IDF. + + ## Options + + * `--atomvm-path` - Path to local AtomVM repository (required if --atomvm-url not provided) + * `--atomvm-url` - Git URL to clone AtomVM from (optional, overrides path) + * `--branch` - Git branch to checkout (default: main) + * `--chip` - Target chip (default: esp32s3, options: esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2, esp32p4) + * `--clean` - Clean build directory before building + + ## Examples + + # Build from local repository + mix atomvm.esp32.build --atomvm-path /path/to/AtomVM + + # Build from git URL + mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM --branch main + + # Build for specific chip with clean build + mix atomvm.esp32.build --atomvm-path /path/to/AtomVM --chip esp32s3 --clean + + """ + use Mix.Task + + @shortdoc "Build AtomVM for ESP32 from source" + + @default_chip "esp32s3" + @default_branch "main" + + @impl Mix.Task + def run(args) do + {opts, _} = + OptionParser.parse!(args, + strict: [ + atomvm_path: :string, + atomvm_url: :string, + branch: :string, + chip: :string, + clean: :boolean + ] + ) + + atomvm_url = Keyword.get(opts, :atomvm_url) + atomvm_path = Keyword.get(opts, :atomvm_path) + branch = Keyword.get(opts, :branch, @default_branch) + chip = Keyword.get(opts, :chip, @default_chip) + clean = Keyword.get(opts, :clean, false) + + # Require either --atomvm-path or --atomvm-url + atomvm_path = + cond do + atomvm_url -> + clone_or_update_repo(atomvm_url, branch) + + atomvm_path -> + atomvm_path + + true -> + IO.puts(""" + Error: Either --atomvm-path or --atomvm-url must be provided + + Examples: + mix atomvm.esp32.build --atomvm-path /path/to/AtomVM + mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM + + """) + + exit({:shutdown, 1}) + end + + # Verify AtomVM path exists + unless File.dir?(atomvm_path) do + IO.puts("Error: AtomVM path does not exist: #{atomvm_path}") + exit({:shutdown, 1}) + end + + IO.puts(""" + + Building AtomVM for #{chip} from source + Repository: #{atomvm_path} + Chip: #{chip} + Clean build: #{clean} + + """) + + with :ok <- check_esp_idf(), + :ok <- build_generic_unix(atomvm_path), + :ok <- build_atomvm(atomvm_path, chip, clean) do + build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) + atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) + + if File.exists?(atomvm_img) do + IO.puts(""" + + ✅ Successfully built AtomVM for #{chip} + + Build directory: #{build_dir} + + Flashable image: #{atomvm_img} + + To flash to your device, run: + mix atomvm.esp32.build.flash --image #{atomvm_img} + + Or use idf.py: + cd #{Path.dirname(build_dir)} + idf.py flash + + """) + else + IO.puts(""" + + ⚠️ Build completed but image file not found at expected location: + #{atomvm_img} + + Please check the build output above for the actual location. + + """) + end + else + {:error, reason} -> + IO.puts("Error: #{reason}") + exit({:shutdown, 1}) + end + end + + defp clone_or_update_repo(url, branch) do + cache_dir = + if Code.ensure_loaded?(Mix.Project) do + Path.join(Path.dirname(Mix.Project.build_path()), "atomvm_source") + else + Path.expand("_build/atomvm_source") + end + + repo_name = url |> Path.basename() |> String.replace(".git", "") + repo_path = Path.join(cache_dir, repo_name) + + if File.dir?(Path.join(repo_path, ".git")) do + IO.puts("Updating existing repository at #{repo_path}") + + {output, status} = + System.cmd("git", ["pull", "origin", branch], + cd: repo_path, + stderr_to_stdout: true + ) + + case status do + 0 -> + IO.puts(output) + repo_path + + _ -> + IO.puts("Error updating repository:\n#{output}") + exit({:shutdown, 1}) + end + else + IO.puts("Cloning #{url} (branch: #{branch})") + File.mkdir_p!(cache_dir) + + {output, status} = + System.cmd("git", ["clone", "--branch", branch, url, repo_path], + stderr_to_stdout: true + ) + + case status do + 0 -> + IO.puts(output) + repo_path + + _ -> + IO.puts("Error cloning repository:\n#{output}") + exit({:shutdown, 1}) + end + end + end + + defp check_esp_idf do + case System.find_executable("idf.py") do + nil -> + {:error, + """ + ESP-IDF not found. Please install and set up ESP-IDF: + + https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/ + """} + + idf_path -> + IO.puts("Found ESP-IDF: #{idf_path}") + :ok + end + end + + defp build_generic_unix(atomvm_path) do + build_dir = Path.join(atomvm_path, "build") + + # Check if tools and esp32boot already exist + packbeam_path = Path.join([build_dir, "tools", "packbeam", "PackBEAM"]) + esp32boot_path = Path.join([build_dir, "libs", "esp32boot", "esp32boot.avm"]) + + if File.exists?(packbeam_path) and File.exists?(esp32boot_path) do + IO.puts("Generic Unix build tools and esp32boot already exist, skipping...") + :ok + else + IO.puts("Building generic Unix tools and esp32boot (required for ESP32 build)...") + File.mkdir_p!(build_dir) + + # Run cmake + {_output, status} = + System.cmd("cmake", ["..", "-DCMAKE_BUILD_TYPE=Release"], + cd: build_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + + case status do + 0 -> + IO.puts("Building tools and esp32boot...") + + {_output, status} = + System.cmd("make", ["PackBEAM", "esp32boot", "exavmlib", "atomvmlib"], + cd: build_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + + case status do + 0 -> + IO.puts("Generic Unix tools and esp32boot built successfully") + :ok + + _ -> + {:error, "Failed to build generic Unix tools"} + end + + _ -> + {:error, "Failed to configure generic Unix build"} + end + end + end + + defp build_atomvm(atomvm_path, chip, clean) do + build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) + platform_dir = Path.join([atomvm_path, "src", "platforms", "esp32"]) + + if clean and File.dir?(build_dir) do + IO.puts("Cleaning build directory...") + File.rm_rf!(build_dir) + end + + IO.puts("Configuring build for #{chip}...") + + # Set target chip + {_output, status} = + System.cmd("idf.py", ["set-target", chip], + cd: platform_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + + case status do + 0 -> + IO.puts("Building AtomVM... (this may take several minutes)") + + {_output, status} = + System.cmd("idf.py", ["build"], + cd: platform_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + + case status do + 0 -> + IO.puts("Creating flashable image...") + + mkimage_script = Path.join([build_dir, "mkimage.sh"]) + + {_output, status} = + System.cmd("sh", [mkimage_script], + cd: build_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + + case status do + 0 -> + :ok + + _ -> + {:error, "Failed to create image"} + end + + _ -> + {:error, "Build failed"} + end + + _ -> + {:error, "Failed to set target chip"} + end + end +end diff --git a/lib/mix/tasks/esp32.build.flash.ex b/lib/mix/tasks/esp32.build.flash.ex new file mode 100644 index 0000000..6d2d37d --- /dev/null +++ b/lib/mix/tasks/esp32.build.flash.ex @@ -0,0 +1,147 @@ +defmodule Mix.Tasks.Atomvm.Esp32.Build.Flash do + @moduledoc """ + Mix task for flashing a custom-built AtomVM image to ESP32. + + ## Options + + * `--image` - Path to the AtomVM .img file to flash (required) + * `--port` - Serial port to use (optional, will auto-detect if not provided) + * `--baud` - Baud rate for flashing (default: 921600) + * `--erase` - Erase flash before flashing (default: false) + + ## Examples + + # Flash with auto-detection + mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img + + # Flash with erase and custom baud rate + mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img --erase --baud 115200 + + # Flash to specific port + mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img --port /dev/ttyUSB0 + + """ + use Mix.Task + + @shortdoc "Flash custom-built AtomVM image to ESP32" + + alias ExAtomVM.EsptoolHelper + + @impl Mix.Task + def run(args) do + {opts, _} = + OptionParser.parse!(args, + strict: [ + image: :string, + port: :string, + baud: :string, + erase: :boolean + ] + ) + + image_path = Keyword.get(opts, :image) + port = Keyword.get(opts, :port) + baud = Keyword.get(opts, :baud, "921600") + erase = Keyword.get(opts, :erase, false) + + unless image_path do + IO.puts(""" + Error: --image option is required + + Usage: + mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img + + """) + + exit({:shutdown, 1}) + end + + unless File.exists?(image_path) do + IO.puts("Error: Image file not found: #{image_path}") + exit({:shutdown, 1}) + end + + with :ok <- EsptoolHelper.setup() do + device = + if port do + # Use specified port + %{"port" => port, "chip_family_name" => "Custom"} + else + # Auto-detect device + EsptoolHelper.select_device() + end + + flash_offset = detect_flash_offset(device["chip_family_name"]) + + IO.puts(""" + + Flashing AtomVM to #{device["chip_family_name"]} + Port: #{device["port"]} + Image: #{image_path} + Flash offset: #{flash_offset} + Baud rate: #{baud} + + """) + + if erase do + IO.puts("Erasing flash...") + + EsptoolHelper.erase_flash([ + "--port", + device["port"], + "--chip", + "auto", + "--after", + "no-reset" + ]) + + :timer.sleep(3000) + end + + tool_args = [ + "--chip", + "auto", + "--port", + device["port"], + "--baud", + baud, + "write-flash", + flash_offset, + image_path + ] + + case EsptoolHelper.flash_pythonx(tool_args) do + true -> + IO.puts(""" + + ✅ Successfully flashed AtomVM to #{device["chip_family_name"]} + + Your project can now be flashed with: + mix atomvm.esp32.flash + + """) + + false -> + IO.puts("Error: Flash failed") + exit({:shutdown, 1}) + end + else + {:error, reason} -> + IO.puts("Error: #{inspect(reason)}") + exit({:shutdown, 1}) + end + end + + defp detect_flash_offset(chip_family) do + %{ + "ESP32" => "0x1000", + "ESP32-S2" => "0x1000", + "ESP32-S3" => "0x0", + "ESP32-C2" => "0x0", + "ESP32-C3" => "0x0", + "ESP32-C6" => "0x0", + "ESP32-H2" => "0x0", + "ESP32-P4" => "0x2000" + }[chip_family] || "0x0" + end +end From b7234f508b0dec8a531ee4e5d7f2a8bb5cd83de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Sun, 28 Dec 2025 12:01:25 +0100 Subject: [PATCH 02/15] fix most comments --- lib/mix/tasks/esp32.build.ex | 174 ++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 56 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index fde90e4..4504783 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -6,10 +6,11 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do ## Options - * `--atomvm-path` - Path to local AtomVM repository (required if --atomvm-url not provided) - * `--atomvm-url` - Git URL to clone AtomVM from (optional, overrides path) - * `--branch` - Git branch to checkout (default: main) - * `--chip` - Target chip (default: esp32s3, options: esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2, esp32p4) + * `--atomvm-path` - Path to local AtomVM repository (optional, overrides URL if both provided) + * `--atomvm-url` - Git URL to clone AtomVM from (optional, defaults to AtomVM/AtomVM main branch) + * `--ref` - Git reference to checkout - branch, tag, or commit SHA (default: main) + * `--chip` - Target chip (default: esp32, options: esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2, esp32p4) + * `--idf-path` - Path to idf.py executable (default: idf.py) * `--clean` - Clean build directory before building ## Examples @@ -18,7 +19,13 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do mix atomvm.esp32.build --atomvm-path /path/to/AtomVM # Build from git URL - mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM --branch main + mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM --ref main + + # Build from specific tag + mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM --ref v0.6.5 + + # Build from specific commit + mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM --ref abc123def # Build for specific chip with clean build mix atomvm.esp32.build --atomvm-path /path/to/AtomVM --chip esp32s3 --clean @@ -28,8 +35,10 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do @shortdoc "Build AtomVM for ESP32 from source" - @default_chip "esp32s3" - @default_branch "main" + @default_chip "esp32" + @default_ref "main" + @default_atomvm_url "https://github.com/atomvm/AtomVM" + @default_idf_path "idf.py" @impl Mix.Task def run(args) do @@ -38,38 +47,28 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do strict: [ atomvm_path: :string, atomvm_url: :string, - branch: :string, + ref: :string, chip: :string, + idf_path: :string, clean: :boolean ] ) - atomvm_url = Keyword.get(opts, :atomvm_url) atomvm_path = Keyword.get(opts, :atomvm_path) - branch = Keyword.get(opts, :branch, @default_branch) + atomvm_url = Keyword.get(opts, :atomvm_url, @default_atomvm_url) + ref = Keyword.get(opts, :ref, @default_ref) chip = Keyword.get(opts, :chip, @default_chip) + idf_path = Keyword.get(opts, :idf_path, @default_idf_path) clean = Keyword.get(opts, :clean, false) - # Require either --atomvm-path or --atomvm-url + # Use --atomvm-path, --atomvm-url, or default to AtomVM/AtomVM main branch atomvm_path = cond do - atomvm_url -> - clone_or_update_repo(atomvm_url, branch) - atomvm_path -> atomvm_path - true -> - IO.puts(""" - Error: Either --atomvm-path or --atomvm-url must be provided - - Examples: - mix atomvm.esp32.build --atomvm-path /path/to/AtomVM - mix atomvm.esp32.build --atomvm-url https://github.com/atomvm/AtomVM - - """) - - exit({:shutdown, 1}) + atomvm_url -> + clone_or_update_repo(atomvm_url, ref) end # Verify AtomVM path exists @@ -87,9 +86,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do """) - with :ok <- check_esp_idf(), + with :ok <- check_esp_idf(idf_path), :ok <- build_generic_unix(atomvm_path), - :ok <- build_atomvm(atomvm_path, chip, clean) do + :ok <- build_atomvm(atomvm_path, chip, idf_path, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) @@ -127,7 +126,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp clone_or_update_repo(url, branch) do + defp clone_or_update_repo(url, ref) do cache_dir = if Code.ensure_loaded?(Mix.Project) do Path.join(Path.dirname(Mix.Project.build_path()), "atomvm_source") @@ -139,29 +138,86 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do repo_path = Path.join(cache_dir, repo_name) if File.dir?(Path.join(repo_path, ".git")) do - IO.puts("Updating existing repository at #{repo_path}") + update_repo(repo_path, ref) + else + clone_repo(url, repo_path, cache_dir, ref) + end + end - {output, status} = - System.cmd("git", ["pull", "origin", branch], - cd: repo_path, - stderr_to_stdout: true - ) + defp clone_repo(url, repo_path, cache_dir, ref) do + IO.puts("Cloning #{url}") + File.mkdir_p!(cache_dir) - case status do - 0 -> - IO.puts(output) - repo_path + {output, status} = + System.cmd("git", ["clone", url, repo_path], stderr_to_stdout: true) - _ -> - IO.puts("Error updating repository:\n#{output}") - exit({:shutdown, 1}) - end - else - IO.puts("Cloning #{url} (branch: #{branch})") - File.mkdir_p!(cache_dir) + case status do + 0 -> + IO.puts(output) + checkout_ref(repo_path, ref) + + _ -> + IO.puts("Error cloning repository:\n#{output}") + exit({:shutdown, 1}) + end + end + + defp update_repo(repo_path, ref) do + IO.puts("Updating existing repository at #{repo_path}") + + # Fetch all refs from origin + {output, status} = + System.cmd("git", ["fetch", "origin"], + cd: repo_path, + stderr_to_stdout: true + ) + + case status do + 0 -> + IO.puts(output) + checkout_ref(repo_path, ref) + + _ -> + IO.puts("Error fetching from repository:\n#{output}") + exit({:shutdown, 1}) + end + end + + defp checkout_ref(repo_path, ref) do + IO.puts("Checking out ref: #{ref}") + + {output, status} = + System.cmd("git", ["checkout", ref], + cd: repo_path, + stderr_to_stdout: true + ) + + case status do + 0 -> + IO.puts(output) + pull_if_branch(repo_path, ref) + + _ -> + IO.puts("Error checking out ref:\n#{output}") + exit({:shutdown, 1}) + end + end + + defp pull_if_branch(repo_path, ref) do + # Check if we're on a branch (not a detached HEAD) + {_output, status} = + System.cmd("git", ["symbolic-ref", "-q", "HEAD"], + cd: repo_path, + stderr_to_stdout: true + ) + + if status == 0 do + # We're on a branch, pull latest changes + IO.puts("Pulling latest changes for branch #{ref}") {output, status} = - System.cmd("git", ["clone", "--branch", branch, url, repo_path], + System.cmd("git", ["pull", "origin", ref], + cd: repo_path, stderr_to_stdout: true ) @@ -171,14 +227,18 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do repo_path _ -> - IO.puts("Error cloning repository:\n#{output}") - exit({:shutdown, 1}) + IO.puts("Warning: Could not pull changes:\n#{output}") + repo_path end + else + # Detached HEAD (tag or commit), no need to pull + IO.puts("Checked out tag or commit (detached HEAD)") + repo_path end end - defp check_esp_idf do - case System.find_executable("idf.py") do + defp check_esp_idf(idf_path) do + case System.find_executable(idf_path) do nil -> {:error, """ @@ -209,7 +269,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Run cmake {_output, status} = - System.cmd("cmake", ["..", "-DCMAKE_BUILD_TYPE=Release"], + System.cmd("cmake", ["..", "-DCMAKE_BUILD_TYPE=Release", "-DAVM_BUILD_RUNTIME_ONLY=ON"], cd: build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) @@ -220,7 +280,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Building tools and esp32boot...") {_output, status} = - System.cmd("make", ["PackBEAM", "esp32boot", "exavmlib", "atomvmlib"], + System.cmd("make", ["PackBEAM", "esp32boot", "elixir_esp32boot", "exavmlib", "atomvmlib"], cd: build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) @@ -241,7 +301,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_atomvm(atomvm_path, chip, clean) do + defp build_atomvm(atomvm_path, chip, idf_path, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) platform_dir = Path.join([atomvm_path, "src", "platforms", "esp32"]) @@ -254,7 +314,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Set target chip {_output, status} = - System.cmd("idf.py", ["set-target", chip], + System.cmd(idf_path, ["set-target", chip], cd: platform_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) @@ -265,7 +325,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Building AtomVM... (this may take several minutes)") {_output, status} = - System.cmd("idf.py", ["build"], + System.cmd(idf_path, ["build"], cd: platform_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) @@ -276,9 +336,11 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Creating flashable image...") mkimage_script = Path.join([build_dir, "mkimage.sh"]) + # TODO: Remove --boot flag when AtomVM#1163 is merged + boot_avm = Path.join([atomvm_path, "build", "libs", "esp32boot", "elixir_esp32boot.avm"]) {_output, status} = - System.cmd("sh", [mkimage_script], + System.cmd("sh", [mkimage_script, "--boot", boot_avm], cd: build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) From af50277d5b5b2eb2c484b79cdea8a38d68b8d751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Sun, 28 Dec 2025 16:45:20 +0100 Subject: [PATCH 03/15] add docker feature --- lib/mix/tasks/esp32.build.ex | 164 +++++++++++++++++++++++++++++------ 1 file changed, 136 insertions(+), 28 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 4504783..6b3def0 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -11,6 +11,8 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do * `--ref` - Git reference to checkout - branch, tag, or commit SHA (default: main) * `--chip` - Target chip (default: esp32, options: esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2, esp32p4) * `--idf-path` - Path to idf.py executable (default: idf.py) + * `--use-docker` - Use ESP-IDF Docker image instead of local installation + * `--idf-version` - ESP-IDF version for Docker image (default: v5.4.1) * `--clean` - Clean build directory before building ## Examples @@ -30,6 +32,12 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Build for specific chip with clean build mix atomvm.esp32.build --atomvm-path /path/to/AtomVM --chip esp32s3 --clean + # Build using Docker (relative path with ./ is important) + mix atomvm.esp32.build --atomvm-path ./_build/atomvm_source/AtomVM/ --use-docker --chip esp32s3 + + # Build using Docker with specific IDF version + mix atomvm.esp32.build --atomvm-path ./_build/atomvm_source/AtomVM/ --use-docker --idf-version v5.4.1 --chip esp32s3 + """ use Mix.Task @@ -39,6 +47,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do @default_ref "main" @default_atomvm_url "https://github.com/atomvm/AtomVM" @default_idf_path "idf.py" + @default_idf_version "v5.4.1" @impl Mix.Task def run(args) do @@ -50,6 +59,8 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do ref: :string, chip: :string, idf_path: :string, + use_docker: :boolean, + idf_version: :string, clean: :boolean ] ) @@ -59,6 +70,8 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do ref = Keyword.get(opts, :ref, @default_ref) chip = Keyword.get(opts, :chip, @default_chip) idf_path = Keyword.get(opts, :idf_path, @default_idf_path) + use_docker = Keyword.get(opts, :use_docker, false) + idf_version = Keyword.get(opts, :idf_version, @default_idf_version) clean = Keyword.get(opts, :clean, false) # Use --atomvm-path, --atomvm-url, or default to AtomVM/AtomVM main branch @@ -86,9 +99,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do """) - with :ok <- check_esp_idf(idf_path), + with :ok <- check_esp_idf(idf_path, use_docker, idf_version), :ok <- build_generic_unix(atomvm_path), - :ok <- build_atomvm(atomvm_path, chip, idf_path, clean) do + :ok <- build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) @@ -237,19 +250,38 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp check_esp_idf(idf_path) do - case System.find_executable(idf_path) do - nil -> - {:error, - """ - ESP-IDF not found. Please install and set up ESP-IDF: + defp check_esp_idf(idf_path, use_docker, idf_version) do + if use_docker do + case System.find_executable("docker") do + nil -> + {:error, + """ + Docker not found. Please install Docker: + + https://docs.docker.com/get-docker/ + """} + + docker_path -> + IO.puts("Found Docker: #{docker_path}") + IO.puts("Using ESP-IDF Docker image: espressif/idf:#{idf_version}") + :ok + end + else + case System.find_executable(idf_path) do + nil -> + {:error, + """ + ESP-IDF not found. Please install and set up ESP-IDF: - https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/ - """} + https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/ - idf_path -> - IO.puts("Found ESP-IDF: #{idf_path}") - :ok + Or use --use-docker to build with Docker instead. + """} + + idf_path_found -> + IO.puts("Found ESP-IDF: #{idf_path_found}") + :ok + end end end @@ -301,7 +333,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_atomvm(atomvm_path, chip, idf_path, clean) do + defp build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) platform_dir = Path.join([atomvm_path, "src", "platforms", "esp32"]) @@ -314,34 +346,51 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Set target chip {_output, status} = - System.cmd(idf_path, ["set-target", chip], - cd: platform_dir, - stderr_to_stdout: true, - into: IO.stream(:stdio, :line) - ) + if use_docker do + run_idf_docker(idf_version, atomvm_path, platform_dir, ["set-target", chip]) + else + System.cmd(idf_path, ["set-target", chip], + cd: platform_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + end case status do 0 -> IO.puts("Building AtomVM... (this may take several minutes)") {_output, status} = - System.cmd(idf_path, ["build"], - cd: platform_dir, - stderr_to_stdout: true, - into: IO.stream(:stdio, :line) - ) + if use_docker do + run_idf_docker(idf_version, atomvm_path, platform_dir, ["build"]) + else + System.cmd(idf_path, ["build"], + cd: platform_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + end case status do 0 -> - IO.puts("Creating flashable image...") + # Use absolute paths to avoid issues with relative paths + abs_atomvm_path = Path.expand(atomvm_path) + abs_build_dir = Path.expand(build_dir) + mkimage_script = Path.join([abs_build_dir, "mkimage.sh"]) + + # Fix paths in mkimage.sh if built with Docker + if use_docker do + IO.puts("Fixing paths in mkimage.sh for host execution...") + fix_mkimage_paths(mkimage_script, abs_atomvm_path) + end - mkimage_script = Path.join([build_dir, "mkimage.sh"]) + IO.puts("Creating flashable image...") # TODO: Remove --boot flag when AtomVM#1163 is merged - boot_avm = Path.join([atomvm_path, "build", "libs", "esp32boot", "elixir_esp32boot.avm"]) + boot_avm = Path.join([abs_atomvm_path, "build", "libs", "esp32boot", "elixir_esp32boot.avm"]) {_output, status} = System.cmd("sh", [mkimage_script, "--boot", boot_avm], - cd: build_dir, + cd: abs_build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) ) @@ -362,4 +411,63 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do {:error, "Failed to set target chip"} end end + + defp fix_mkimage_paths(mkimage_script, atomvm_path) do + # Convert /project paths to actual host paths + # Files generated in Docker contain /project/ paths that need to be replaced + abs_atomvm_path = Path.expand(atomvm_path) + build_dir = Path.dirname(mkimage_script) + + IO.puts("Fixing container paths in build files...") + + # Find ALL files containing /project and fix them + # Use grep to find files, then sed to replace paths + {output, status} = + System.cmd( + "sh", + [ + "-c", + "grep -rl '/project' #{build_dir} 2>/dev/null | xargs -r sed -i 's|/project|#{abs_atomvm_path}|g'" + ], + stderr_to_stdout: true + ) + + case status do + 0 -> + IO.puts("Successfully fixed container paths") + :ok + + 1 -> + # Exit code 1 from grep means no matches found, which is fine + IO.puts("No container paths found to fix") + :ok + + _ -> + IO.puts("Warning: Some files may not have been fixed: #{output}") + :ok + end + end + + defp run_idf_docker(idf_version, atomvm_path, platform_dir, idf_args) do + # Calculate the relative path from atomvm_path to platform_dir + relative_dir = Path.relative_to(platform_dir, atomvm_path) + + # Build docker command + docker_args = [ + "run", + "--rm", + "-v", + "#{atomvm_path}:/project", + "-w", + "/project/#{relative_dir}", + "espressif/idf:#{idf_version}", + "idf.py" + ] ++ idf_args + + System.cmd("docker", docker_args, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + end + end From 5f8ddf0ea45744b6b87e7b89c1131c050a2c96c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Mon, 29 Dec 2025 23:23:31 +0100 Subject: [PATCH 04/15] address comments from Peter --- lib/mix/tasks/esp32.build.flash.ex | 147 ----------------------------- lib/mix/tasks/esp32.install.ex | 82 ++++++++++++++-- 2 files changed, 76 insertions(+), 153 deletions(-) delete mode 100644 lib/mix/tasks/esp32.build.flash.ex diff --git a/lib/mix/tasks/esp32.build.flash.ex b/lib/mix/tasks/esp32.build.flash.ex deleted file mode 100644 index 6d2d37d..0000000 --- a/lib/mix/tasks/esp32.build.flash.ex +++ /dev/null @@ -1,147 +0,0 @@ -defmodule Mix.Tasks.Atomvm.Esp32.Build.Flash do - @moduledoc """ - Mix task for flashing a custom-built AtomVM image to ESP32. - - ## Options - - * `--image` - Path to the AtomVM .img file to flash (required) - * `--port` - Serial port to use (optional, will auto-detect if not provided) - * `--baud` - Baud rate for flashing (default: 921600) - * `--erase` - Erase flash before flashing (default: false) - - ## Examples - - # Flash with auto-detection - mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img - - # Flash with erase and custom baud rate - mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img --erase --baud 115200 - - # Flash to specific port - mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img --port /dev/ttyUSB0 - - """ - use Mix.Task - - @shortdoc "Flash custom-built AtomVM image to ESP32" - - alias ExAtomVM.EsptoolHelper - - @impl Mix.Task - def run(args) do - {opts, _} = - OptionParser.parse!(args, - strict: [ - image: :string, - port: :string, - baud: :string, - erase: :boolean - ] - ) - - image_path = Keyword.get(opts, :image) - port = Keyword.get(opts, :port) - baud = Keyword.get(opts, :baud, "921600") - erase = Keyword.get(opts, :erase, false) - - unless image_path do - IO.puts(""" - Error: --image option is required - - Usage: - mix atomvm.esp32.build.flash --image /path/to/AtomVM-esp32s3.img - - """) - - exit({:shutdown, 1}) - end - - unless File.exists?(image_path) do - IO.puts("Error: Image file not found: #{image_path}") - exit({:shutdown, 1}) - end - - with :ok <- EsptoolHelper.setup() do - device = - if port do - # Use specified port - %{"port" => port, "chip_family_name" => "Custom"} - else - # Auto-detect device - EsptoolHelper.select_device() - end - - flash_offset = detect_flash_offset(device["chip_family_name"]) - - IO.puts(""" - - Flashing AtomVM to #{device["chip_family_name"]} - Port: #{device["port"]} - Image: #{image_path} - Flash offset: #{flash_offset} - Baud rate: #{baud} - - """) - - if erase do - IO.puts("Erasing flash...") - - EsptoolHelper.erase_flash([ - "--port", - device["port"], - "--chip", - "auto", - "--after", - "no-reset" - ]) - - :timer.sleep(3000) - end - - tool_args = [ - "--chip", - "auto", - "--port", - device["port"], - "--baud", - baud, - "write-flash", - flash_offset, - image_path - ] - - case EsptoolHelper.flash_pythonx(tool_args) do - true -> - IO.puts(""" - - ✅ Successfully flashed AtomVM to #{device["chip_family_name"]} - - Your project can now be flashed with: - mix atomvm.esp32.flash - - """) - - false -> - IO.puts("Error: Flash failed") - exit({:shutdown, 1}) - end - else - {:error, reason} -> - IO.puts("Error: #{inspect(reason)}") - exit({:shutdown, 1}) - end - end - - defp detect_flash_offset(chip_family) do - %{ - "ESP32" => "0x1000", - "ESP32-S2" => "0x1000", - "ESP32-S3" => "0x0", - "ESP32-C2" => "0x0", - "ESP32-C3" => "0x0", - "ESP32-C6" => "0x0", - "ESP32-H2" => "0x0", - "ESP32-P4" => "0x2000" - }[chip_family] || "0x0" - end -end diff --git a/lib/mix/tasks/esp32.install.ex b/lib/mix/tasks/esp32.install.ex index 5974f2f..b5fb7ac 100644 --- a/lib/mix/tasks/esp32.install.ex +++ b/lib/mix/tasks/esp32.install.ex @@ -1,22 +1,52 @@ defmodule Mix.Tasks.Atomvm.Esp32.Install do @moduledoc """ - Mix task for erasing flash and installing the latest AtomVM release to connected device. + Mix task for erasing flash and installing AtomVM to connected device. - Takes an optional --baud option to set the baud rate of the flashing. - Defaults to 921600, use 115200 for slower devices. + By default, downloads and installs the latest AtomVM release from GitHub. + Optionally, can install a custom-built image using the --image option. + + **WARNING:** This task erases the current flash before installing. + + ## Options + + * `--image` - Path to custom AtomVM .img file (optional, downloads latest release if not provided) + * `--baud` - Baud rate for flashing (default: 921600, use 115200 for slower devices) + + ## Examples + + # Install latest release from GitHub (erases flash) + mix atomvm.esp32.install + + # Install custom-built image (erases flash) + mix atomvm.esp32.install --image /path/to/AtomVM-esp32s3.img + + # Install with custom baud rate + mix atomvm.esp32.install --baud 115200 After install, your project can be flashed with: mix atomvm.esp32.flash """ use Mix.Task - @shortdoc "Install latest AtomVM release on ESP32" + @shortdoc "Install AtomVM to ESP32 device" alias ExAtomVM.EsptoolHelper @impl Mix.Task def run(args) do - {opts, _} = OptionParser.parse!(args, strict: [baud: :string]) + {opts, _} = OptionParser.parse!(args, strict: [image: :string, baud: :string]) + + case Keyword.get(opts, :image) do + nil -> + run_with_latest_release(opts) + + image_path -> + run_with_custom_image(image_path, opts) + end + end + + # Install latest release from GitHub + defp run_with_latest_release(opts) do baud = Keyword.get(opts, :baud, "921600") with :ok <- check_req_dependency(), @@ -58,13 +88,51 @@ defmodule Mix.Tasks.Atomvm.Esp32.Install do end end + # Install custom-built image + defp run_with_custom_image(image_path, opts) do + baud = Keyword.get(opts, :baud, "921600") + + if not File.exists?(image_path) do + IO.puts("Error: Image file not found: #{image_path}") + exit({:shutdown, 1}) + end + + with :ok <- EsptoolHelper.setup(), + selected_device <- EsptoolHelper.select_device(), + :ok <- confirm_erase_and_flash(selected_device, image_path), + true <- + EsptoolHelper.erase_flash([ + "--port", + selected_device["port"], + "--chip", + "auto", + "--after", + "no-reset" + ]), + :timer.sleep(3000), + true <- flash_release(selected_device, image_path, baud) do + IO.puts(""" + + Successfully installed AtomVM on #{selected_device["chip_family_name"]} Port: #{selected_device["port"]} MAC: #{selected_device["mac_address"]} + + Your project can be flashed with: + mix atomvm.esp32.flash + + """) + else + {:error, reason} -> + IO.puts("Error: #{reason}") + exit({:shutdown, 1}) + end + end + defp confirm_erase_and_flash(selected_device, release_file) do confirmation = IO.gets(""" Are you sure you want to erase the flash of #{selected_device["chip_family_name"]} - Port: #{selected_device["port"]} MAC: #{selected_device["mac_address"]} - And install AtomVM: #{release_file} + And install AtomVM: #{Path.basename(release_file)} ? [N/y]: """) @@ -149,7 +217,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Install do "ESP32-S3" => "0x0", "ESP32-C2" => "0x0", "ESP32-C3" => "0x0", + "ESP32-C5" => "0x2000", "ESP32-C6" => "0x0", + "ESP32-C61" => "0x0", "ESP32-H2" => "0x0", "ESP32-P4" => "0x2000" }[device["chip_family_name"]] || "0x0" From 586ed3c3d8c2dffc934d329495bf98df3af40d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Tue, 30 Dec 2025 01:25:23 +0100 Subject: [PATCH 05/15] fix build only elixir esp32 boot --- lib/mix/tasks/esp32.build.ex | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 6b3def0..7a70c83 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -290,13 +290,13 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Check if tools and esp32boot already exist packbeam_path = Path.join([build_dir, "tools", "packbeam", "PackBEAM"]) - esp32boot_path = Path.join([build_dir, "libs", "esp32boot", "esp32boot.avm"]) + esp32boot_path = Path.join([build_dir, "libs", "esp32boot", "elixir_esp32boot.avm"]) if File.exists?(packbeam_path) and File.exists?(esp32boot_path) do - IO.puts("Generic Unix build tools and esp32boot already exist, skipping...") + IO.puts("Generic Unix build tools and elixir_esp32boot already exist, skipping...") :ok else - IO.puts("Building generic Unix tools and esp32boot (required for ESP32 build)...") + IO.puts("Building generic Unix tools and elixir_esp32boot (required for ESP32 build)...") File.mkdir_p!(build_dir) # Run cmake @@ -309,10 +309,10 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do case status do 0 -> - IO.puts("Building tools and esp32boot...") + IO.puts("Building tools and elixir_esp32boot...") {_output, status} = - System.cmd("make", ["PackBEAM", "esp32boot", "elixir_esp32boot", "exavmlib", "atomvmlib"], + System.cmd("make", ["PackBEAM", "elixir_esp32boot", "exavmlib", "atomvmlib"], cd: build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) @@ -320,7 +320,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do case status do 0 -> - IO.puts("Generic Unix tools and esp32boot built successfully") + IO.puts("Generic Unix tools and elixir_esp32boot built successfully") :ok _ -> @@ -358,6 +358,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do case status do 0 -> + # Configure to use Elixir partition table (larger boot partition for elixir_esp32boot.avm) + configure_elixir_partitions(platform_dir) + IO.puts("Building AtomVM... (this may take several minutes)") {_output, status} = From 0b8f5f3c04077ce6b3d6176a1d99145ffc19df4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Tue, 30 Dec 2025 11:00:17 +0100 Subject: [PATCH 06/15] add requeriments in the host, prefer ninja as build tool --- lib/mix/tasks/esp32.build.ex | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 7a70c83..33ff517 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -4,6 +4,20 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do Builds AtomVM from a local repository or git URL using ESP-IDF. + ## Requirements + + **Without Docker:** + * CMake (3.13 or later) + * Ninja (preferred) or Make + * Erlang/OTP (27 or later) + * Elixir (1.18 or later) + * ESP-IDF (v5.4.1 recommended) + * Git + + **With Docker (--use-docker flag):** + * Docker + * Git + ## Options * `--atomvm-path` - Path to local AtomVM repository (optional, overrides URL if both provided) @@ -299,9 +313,24 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Building generic Unix tools and elixir_esp32boot (required for ESP32 build)...") File.mkdir_p!(build_dir) + # Check if ninja is available, fall back to make if not + {build_tool, cmake_generator} = + case System.find_executable("ninja") do + nil -> + IO.puts("Ninja not found, using Make as build system") + {"make", []} + + _ninja_path -> + IO.puts("Using Ninja as build system") + {"ninja", ["-GNinja"]} + end + # Run cmake + cmake_args = + [".."] ++ cmake_generator ++ ["-DCMAKE_BUILD_TYPE=Release", "-DAVM_BUILD_RUNTIME_ONLY=ON"] + {_output, status} = - System.cmd("cmake", ["..", "-DCMAKE_BUILD_TYPE=Release", "-DAVM_BUILD_RUNTIME_ONLY=ON"], + System.cmd("cmake", cmake_args, cd: build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) @@ -312,7 +341,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Building tools and elixir_esp32boot...") {_output, status} = - System.cmd("make", ["PackBEAM", "elixir_esp32boot", "exavmlib", "atomvmlib"], + System.cmd(build_tool, ["PackBEAM", "elixir_esp32boot", "exavmlib", "atomvmlib"], cd: build_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) From 65473b58982d2c18bdc1be74a381ad86423d6925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Tue, 30 Dec 2025 11:19:04 +0100 Subject: [PATCH 07/15] add custom mbedtls path --- lib/mix/tasks/esp32.build.ex | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 33ff517..339c69b 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -28,6 +28,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do * `--use-docker` - Use ESP-IDF Docker image instead of local installation * `--idf-version` - ESP-IDF version for Docker image (default: v5.4.1) * `--clean` - Clean build directory before building + * `--mbedtls-prefix` - Path to custom MbedTLS installation (optional, falls back to MBEDTLS_PREFIX env var) ## Examples @@ -52,6 +53,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Build using Docker with specific IDF version mix atomvm.esp32.build --atomvm-path ./_build/atomvm_source/AtomVM/ --use-docker --idf-version v5.4.1 --chip esp32s3 + # Build with custom MbedTLS + mix atomvm.esp32.build --atomvm-path /path/to/AtomVM --mbedtls-prefix /usr/local/opt/mbedtls@3 + """ use Mix.Task @@ -75,7 +79,8 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do idf_path: :string, use_docker: :boolean, idf_version: :string, - clean: :boolean + clean: :boolean, + mbedtls_prefix: :string ] ) @@ -88,6 +93,10 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do idf_version = Keyword.get(opts, :idf_version, @default_idf_version) clean = Keyword.get(opts, :clean, false) + # Get mbedtls_prefix from option or environment variable + mbedtls_prefix = + Keyword.get(opts, :mbedtls_prefix) || System.get_env("MBEDTLS_PREFIX") + # Use --atomvm-path, --atomvm-url, or default to AtomVM/AtomVM main branch atomvm_path = cond do @@ -114,7 +123,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do """) with :ok <- check_esp_idf(idf_path, use_docker, idf_version), - :ok <- build_generic_unix(atomvm_path), + :ok <- build_generic_unix(atomvm_path, mbedtls_prefix), :ok <- build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) @@ -299,7 +308,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_generic_unix(atomvm_path) do + defp build_generic_unix(atomvm_path, mbedtls_prefix) do build_dir = Path.join(atomvm_path, "build") # Check if tools and esp32boot already exist @@ -325,9 +334,20 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do {"ninja", ["-GNinja"]} end - # Run cmake + # Equivalent to: ${MBEDTLS_PREFIX:+-DCMAKE_PREFIX_PATH="$MBEDTLS_PREFIX"} + mbedtls_args = + if mbedtls_prefix do + IO.puts("Using custom MbedTLS from: #{mbedtls_prefix}") + ["-DCMAKE_PREFIX_PATH=#{mbedtls_prefix}"] + else + [] + end + + # Run cmake: cmake .. ${MBEDTLS_PREFIX:+...} -G Ninja -DCMAKE_BUILD_TYPE=Release -DAVM_BUILD_RUNTIME_ONLY=ON cmake_args = - [".."] ++ cmake_generator ++ ["-DCMAKE_BUILD_TYPE=Release", "-DAVM_BUILD_RUNTIME_ONLY=ON"] + [".."] ++ + mbedtls_args ++ + cmake_generator ++ ["-DCMAKE_BUILD_TYPE=Release", "-DAVM_BUILD_RUNTIME_ONLY=ON"] {_output, status} = System.cmd("cmake", cmake_args, From 8172eb22507692e63cb8a21fbd28bf586ece39e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Tue, 30 Dec 2025 11:28:10 +0100 Subject: [PATCH 08/15] remove bug --- lib/mix/tasks/esp32.build.ex | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 339c69b..c1c9243 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -407,9 +407,6 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do case status do 0 -> - # Configure to use Elixir partition table (larger boot partition for elixir_esp32boot.avm) - configure_elixir_partitions(platform_dir) - IO.puts("Building AtomVM... (this may take several minutes)") {_output, status} = From bc7c78e52b9f5779074a3d0f67f3c78c268f67fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Tue, 30 Dec 2025 11:35:33 +0100 Subject: [PATCH 09/15] change message and partition table --- lib/mix/tasks/esp32.build.ex | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index c1c9243..ac5afc1 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -138,7 +138,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do Flashable image: #{atomvm_img} To flash to your device, run: - mix atomvm.esp32.build.flash --image #{atomvm_img} + mix atomvm.esp32.install --image #{atomvm_img} Or use idf.py: cd #{Path.dirname(build_dir)} @@ -407,13 +407,16 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do case status do 0 -> - IO.puts("Building AtomVM... (this may take several minutes)") + IO.puts("Building AtomVM with Elixir partitions... (this may take several minutes)") + + # Use Elixir partition table (512KB boot partition for elixir_esp32boot.avm) + partition_config = "-DCONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions-elixir.csv\"" {_output, status} = if use_docker do - run_idf_docker(idf_version, atomvm_path, platform_dir, ["build"]) + run_idf_docker(idf_version, atomvm_path, platform_dir, [partition_config, "build"]) else - System.cmd(idf_path, ["build"], + System.cmd(idf_path, [partition_config, "build"], cd: platform_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) From 5b747717a8628e5ae23fb10edb2d21e75b23d4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Tue, 30 Dec 2025 12:35:14 +0100 Subject: [PATCH 10/15] fix build with IDF installed --- lib/mix/tasks/esp32.build.ex | 102 ++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 6 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index ac5afc1..4755ffe 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -124,6 +124,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do with :ok <- check_esp_idf(idf_path, use_docker, idf_version), :ok <- build_generic_unix(atomvm_path, mbedtls_prefix), + :ok <- copy_avm_libraries(atomvm_path), :ok <- build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) @@ -382,6 +383,72 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end + defp configure_elixir_partitions(platform_dir) do + # Per AtomVM docs: Add partition config to sdkconfig.defaults before building + sdkconfig_defaults = platform_dir |> Path.join("sdkconfig.defaults") + + IO.puts("Configuring Elixir partition table (partitions-elixir.csv)...") + + # Read existing defaults or create empty + content = + if File.exists?(sdkconfig_defaults) do + File.read!(sdkconfig_defaults) + else + "" + end + + # Check if partition config already exists + if not String.contains?(content, "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME") do + # Append Elixir partition configuration + new_content = content <> "\nCONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions-elixir.csv\"\n" + File.write!(sdkconfig_defaults, new_content) + IO.puts("✓ Added partitions-elixir.csv to sdkconfig.defaults") + else + # Replace existing config + new_content = + content + |> String.replace( + ~r/CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="[^"]+"/, + ~s(CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-elixir.csv") + ) + + File.write!(sdkconfig_defaults, new_content) + IO.puts("✓ Updated sdkconfig.defaults to use partitions-elixir.csv") + end + end + + defp copy_avm_libraries(atomvm_path) do + avm_deps_dir = File.cwd!() |> Path.join("avm_deps") + + if File.dir?(avm_deps_dir) do + IO.puts("Removing existing avm_deps folder...") + File.rm_rf!(avm_deps_dir) + end + + IO.puts("Creating avm_deps folder and copying libraries...") + File.mkdir_p!(avm_deps_dir) + + build_libs_dir = atomvm_path |> Path.join("build") |> Path.join("libs") + avm_files = build_libs_dir |> Path.join("**/*.avm") |> Path.wildcard() + + # Copy each file + case avm_files do + [] -> + IO.puts("Warning: No .avm files found in #{build_libs_dir}") + :ok + + files -> + Enum.each(files, fn src_path -> + dest_path = src_path |> Path.basename() |> then(&Path.join(avm_deps_dir, &1)) + File.cp!(src_path, dest_path) + IO.puts(" Copied #{Path.basename(src_path)}") + end) + + IO.puts("✓ Copied #{length(files)} AVM libraries to #{avm_deps_dir}") + :ok + end + end + defp build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) platform_dir = Path.join([atomvm_path, "src", "platforms", "esp32"]) @@ -393,6 +460,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Configuring build for #{chip}...") + # Configure Elixir partition table in sdkconfig.defaults BEFORE set-target + configure_elixir_partitions(platform_dir) + # Set target chip {_output, status} = if use_docker do @@ -407,22 +477,42 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do case status do 0 -> - IO.puts("Building AtomVM with Elixir partitions... (this may take several minutes)") - - # Use Elixir partition table (512KB boot partition for elixir_esp32boot.avm) - partition_config = "-DCONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions-elixir.csv\"" + # Reconfigure to ensure partition table settings are applied + IO.puts("Reconfiguring to apply Elixir partitions...") {_output, status} = if use_docker do - run_idf_docker(idf_version, atomvm_path, platform_dir, [partition_config, "build"]) + run_idf_docker(idf_version, atomvm_path, platform_dir, ["reconfigure"]) else - System.cmd(idf_path, [partition_config, "build"], + System.cmd(idf_path, ["reconfigure"], cd: platform_dir, stderr_to_stdout: true, into: IO.stream(:stdio, :line) ) end + status = + case status do + 0 -> + IO.puts("Building AtomVM... (this may take several minutes)") + + {_output, build_status} = + if use_docker do + run_idf_docker(idf_version, atomvm_path, platform_dir, ["build"]) + else + System.cmd(idf_path, ["build"], + cd: platform_dir, + stderr_to_stdout: true, + into: IO.stream(:stdio, :line) + ) + end + + build_status + + _ -> + status + end + case status do 0 -> # Use absolute paths to avoid issues with relative paths From 54fd5ab7b0e4ff547ea2809ec75dfe91059ef9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 2 Jan 2026 11:04:38 +0100 Subject: [PATCH 11/15] remove unecessary things since the path is taken by the script --- lib/mix/tasks/esp32.build.ex | 42 ------------------------------------ 1 file changed, 42 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 4755ffe..5a70f09 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -520,12 +520,6 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do abs_build_dir = Path.expand(build_dir) mkimage_script = Path.join([abs_build_dir, "mkimage.sh"]) - # Fix paths in mkimage.sh if built with Docker - if use_docker do - IO.puts("Fixing paths in mkimage.sh for host execution...") - fix_mkimage_paths(mkimage_script, abs_atomvm_path) - end - IO.puts("Creating flashable image...") # TODO: Remove --boot flag when AtomVM#1163 is merged boot_avm = Path.join([abs_atomvm_path, "build", "libs", "esp32boot", "elixir_esp32boot.avm"]) @@ -554,42 +548,6 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp fix_mkimage_paths(mkimage_script, atomvm_path) do - # Convert /project paths to actual host paths - # Files generated in Docker contain /project/ paths that need to be replaced - abs_atomvm_path = Path.expand(atomvm_path) - build_dir = Path.dirname(mkimage_script) - - IO.puts("Fixing container paths in build files...") - - # Find ALL files containing /project and fix them - # Use grep to find files, then sed to replace paths - {output, status} = - System.cmd( - "sh", - [ - "-c", - "grep -rl '/project' #{build_dir} 2>/dev/null | xargs -r sed -i 's|/project|#{abs_atomvm_path}|g'" - ], - stderr_to_stdout: true - ) - - case status do - 0 -> - IO.puts("Successfully fixed container paths") - :ok - - 1 -> - # Exit code 1 from grep means no matches found, which is fine - IO.puts("No container paths found to fix") - :ok - - _ -> - IO.puts("Warning: Some files may not have been fixed: #{output}") - :ok - end - end - defp run_idf_docker(idf_version, atomvm_path, platform_dir, idf_args) do # Calculate the relative path from atomvm_path to platform_dir relative_dir = Path.relative_to(platform_dir, atomvm_path) From 0363e93d5108aaa0ca913f795ca028ad34686ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 2 Jan 2026 21:37:27 +0100 Subject: [PATCH 12/15] add docker usage note, and address build generic_unix options passing --- lib/mix/tasks/esp32.build.ex | 78 ++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 5a70f09..9658ad5 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -6,17 +6,20 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do ## Requirements + **General requirements** + * Erlang/OTP (27 or later) + * Elixir (1.18 or later) + * Git + **Without Docker:** * CMake (3.13 or later) * Ninja (preferred) or Make - * Erlang/OTP (27 or later) - * Elixir (1.18 or later) * ESP-IDF (v5.4.1 recommended) - * Git **With Docker (--use-docker flag):** * Docker - * Git + * Note: Docker build support requires AtomVM main branch from Jan 2, 2026 or later (PR #2052). + Previous AtomVM versions must be built with the local ESP-IDF toolchain installed. ## Options @@ -123,9 +126,22 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do """) with :ok <- check_esp_idf(idf_path, use_docker, idf_version), - :ok <- build_generic_unix(atomvm_path, mbedtls_prefix), + :ok <- + build_generic_unix( + atomvm_path: atomvm_path, + mbedtls_prefix: mbedtls_prefix, + clean: clean + ), :ok <- copy_avm_libraries(atomvm_path), - :ok <- build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do + :ok <- + build_atomvm( + atomvm_path: atomvm_path, + chip: chip, + idf_path: idf_path, + use_docker: use_docker, + idf_version: idf_version, + clean: clean + ) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) @@ -309,9 +325,19 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_generic_unix(atomvm_path, mbedtls_prefix) do + defp build_generic_unix(opts) do + atomvm_path = Keyword.fetch!(opts, :atomvm_path) + mbedtls_prefix = Keyword.get(opts, :mbedtls_prefix) + clean = Keyword.get(opts, :clean, false) + build_dir = Path.join(atomvm_path, "build") + # Clean build directory if requested + if clean and File.dir?(build_dir) do + IO.puts("Cleaning generic Unix build directory...") + File.rm_rf!(build_dir) + end + # Check if tools and esp32boot already exist packbeam_path = Path.join([build_dir, "tools", "packbeam", "PackBEAM"]) esp32boot_path = Path.join([build_dir, "libs", "esp32boot", "elixir_esp32boot.avm"]) @@ -400,7 +426,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do # Check if partition config already exists if not String.contains?(content, "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME") do # Append Elixir partition configuration - new_content = content <> "\nCONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions-elixir.csv\"\n" + new_content = + content <> "\nCONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions-elixir.csv\"\n" + File.write!(sdkconfig_defaults, new_content) IO.puts("✓ Added partitions-elixir.csv to sdkconfig.defaults") else @@ -449,7 +477,14 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_atomvm(atomvm_path, chip, idf_path, use_docker, idf_version, clean) do + defp build_atomvm(opts) do + atomvm_path = Keyword.fetch!(opts, :atomvm_path) + chip = Keyword.fetch!(opts, :chip) + idf_path = Keyword.fetch!(opts, :idf_path) + use_docker = Keyword.get(opts, :use_docker, false) + idf_version = Keyword.fetch!(opts, :idf_version) + clean = Keyword.get(opts, :clean, false) + build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) platform_dir = Path.join([atomvm_path, "src", "platforms", "esp32"]) @@ -522,7 +557,8 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do IO.puts("Creating flashable image...") # TODO: Remove --boot flag when AtomVM#1163 is merged - boot_avm = Path.join([abs_atomvm_path, "build", "libs", "esp32boot", "elixir_esp32boot.avm"]) + boot_avm = + Path.join([abs_atomvm_path, "build", "libs", "esp32boot", "elixir_esp32boot.avm"]) {_output, status} = System.cmd("sh", [mkimage_script, "--boot", boot_avm], @@ -553,21 +589,21 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do relative_dir = Path.relative_to(platform_dir, atomvm_path) # Build docker command - docker_args = [ - "run", - "--rm", - "-v", - "#{atomvm_path}:/project", - "-w", - "/project/#{relative_dir}", - "espressif/idf:#{idf_version}", - "idf.py" - ] ++ idf_args + docker_args = + [ + "run", + "--rm", + "-v", + "#{atomvm_path}:/project", + "-w", + "/project/#{relative_dir}", + "espressif/idf:#{idf_version}", + "idf.py" + ] ++ idf_args System.cmd("docker", docker_args, stderr_to_stdout: true, into: IO.stream(:stdio, :line) ) end - end From 5395d706ee0a62755bcb5fabff318bdd9ef48cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Fri, 2 Jan 2026 22:10:29 +0100 Subject: [PATCH 13/15] put commit instead of PR --- lib/mix/tasks/esp32.build.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 9658ad5..7b63827 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -18,7 +18,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do **With Docker (--use-docker flag):** * Docker - * Note: Docker build support requires AtomVM main branch from Jan 2, 2026 or later (PR #2052). + * Note: Docker build support requires AtomVM main branch from Jan 2, 2026 or later (https://github.com/atomvm/AtomVM/commit/2a4f0d0fe100ef6d440bef86eabfd08c5b290f6c). Previous AtomVM versions must be built with the local ESP-IDF toolchain installed. ## Options From e1cede235ba8a5cd9f6b57ec61c6badacf61846e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Sat, 3 Jan 2026 19:45:57 +0100 Subject: [PATCH 14/15] replace default version and use arguments instead of opts --- lib/mix/tasks/esp32.build.ex | 38 ++++++++---------------------------- mix.lock | 22 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 mix.lock diff --git a/lib/mix/tasks/esp32.build.ex b/lib/mix/tasks/esp32.build.ex index 7b63827..777383e 100644 --- a/lib/mix/tasks/esp32.build.ex +++ b/lib/mix/tasks/esp32.build.ex @@ -14,7 +14,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do **Without Docker:** * CMake (3.13 or later) * Ninja (preferred) or Make - * ESP-IDF (v5.4.1 recommended) + * ESP-IDF (v5.5.2 recommended) **With Docker (--use-docker flag):** * Docker @@ -29,7 +29,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do * `--chip` - Target chip (default: esp32, options: esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2, esp32p4) * `--idf-path` - Path to idf.py executable (default: idf.py) * `--use-docker` - Use ESP-IDF Docker image instead of local installation - * `--idf-version` - ESP-IDF version for Docker image (default: v5.4.1) + * `--idf-version` - ESP-IDF version for Docker image (default: v5.5.2) * `--clean` - Clean build directory before building * `--mbedtls-prefix` - Path to custom MbedTLS installation (optional, falls back to MBEDTLS_PREFIX env var) @@ -54,7 +54,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do mix atomvm.esp32.build --atomvm-path ./_build/atomvm_source/AtomVM/ --use-docker --chip esp32s3 # Build using Docker with specific IDF version - mix atomvm.esp32.build --atomvm-path ./_build/atomvm_source/AtomVM/ --use-docker --idf-version v5.4.1 --chip esp32s3 + mix atomvm.esp32.build --atomvm-path ./_build/atomvm_source/AtomVM/ --use-docker --idf-version v5.5.2 --chip esp32s3 # Build with custom MbedTLS mix atomvm.esp32.build --atomvm-path /path/to/AtomVM --mbedtls-prefix /usr/local/opt/mbedtls@3 @@ -68,7 +68,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do @default_ref "main" @default_atomvm_url "https://github.com/atomvm/AtomVM" @default_idf_path "idf.py" - @default_idf_version "v5.4.1" + @default_idf_version "v5.5.2" @impl Mix.Task def run(args) do @@ -126,22 +126,9 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do """) with :ok <- check_esp_idf(idf_path, use_docker, idf_version), - :ok <- - build_generic_unix( - atomvm_path: atomvm_path, - mbedtls_prefix: mbedtls_prefix, - clean: clean - ), + :ok <- build_generic_unix(atomvm_path, mbedtls_prefix, clean), :ok <- copy_avm_libraries(atomvm_path), - :ok <- - build_atomvm( - atomvm_path: atomvm_path, - chip: chip, - idf_path: idf_path, - use_docker: use_docker, - idf_version: idf_version, - clean: clean - ) do + :ok <- build_atomvm(atomvm_path, chip, idf_path, idf_version, use_docker, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) atomvm_img = Path.join([build_dir, "atomvm-#{chip}.img"]) @@ -325,10 +312,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_generic_unix(opts) do - atomvm_path = Keyword.fetch!(opts, :atomvm_path) - mbedtls_prefix = Keyword.get(opts, :mbedtls_prefix) - clean = Keyword.get(opts, :clean, false) + defp build_generic_unix(atomvm_path, mbedtls_prefix, clean) do build_dir = Path.join(atomvm_path, "build") @@ -477,13 +461,7 @@ defmodule Mix.Tasks.Atomvm.Esp32.Build do end end - defp build_atomvm(opts) do - atomvm_path = Keyword.fetch!(opts, :atomvm_path) - chip = Keyword.fetch!(opts, :chip) - idf_path = Keyword.fetch!(opts, :idf_path) - use_docker = Keyword.get(opts, :use_docker, false) - idf_version = Keyword.fetch!(opts, :idf_version) - clean = Keyword.get(opts, :clean, false) + defp build_atomvm(atomvm_path, chip, idf_path, idf_version, use_docker, clean) do build_dir = Path.join([atomvm_path, "src", "platforms", "esp32", "build"]) platform_dir = Path.join([atomvm_path, "src", "platforms", "esp32"]) diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..c5012b8 --- /dev/null +++ b/mix.lock @@ -0,0 +1,22 @@ +%{ + "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, + "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, + "ex_doc": {:hex, :ex_doc, "0.39.3", "519c6bc7e84a2918b737aec7ef48b96aa4698342927d080437f61395d361dcee", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "0590955cf7ad3b625780ee1c1ea627c28a78948c6c0a9b0322bd976a079996e1"}, + "finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"}, + "fine": {:hex, :fine, "0.1.4", "b19a89c1476c7c57afb5f9314aed5960b5bc95d5277de4cb5ee8e1d1616ce379", [:mix], [], "hexpm", "be3324cc454a42d80951cf6023b9954e9ff27c6daa255483b3e8d608670303f5"}, + "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, + "pythonx": {:hex, :pythonx, "0.4.7", "604a3a78377abdaa8739c561cb871c856b0e80d25fd057277839912017004af0", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.2", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "20d8b456df995e6ccd6d88dcf118ba80464194515f71a5c89aacdb824d235c52"}, + "req": {:hex, :req, "0.5.16", "99ba6a36b014458e52a8b9a0543bfa752cb0344b2a9d756651db1281d4ba4450", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "974a7a27982b9b791df84e8f6687d21483795882a7840e8309abdbe08bb06f09"}, + "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, + "uf2tool": {:hex, :uf2tool, "1.1.0", "7091931234ca5a256b66ea691983867d51229798622d083f85c1ad779798a734", [:rebar3], [], "hexpm", "1a7e5ca7ef3d19c7a0b0acf3db804b3188e0980884acffa13fd57d733507b73d"}, +} From d6b963f53bf2f2b7f1e3254e21165580443622e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pepe=20M=C3=A1rquez=20Romero?= Date: Mon, 5 Jan 2026 16:31:00 +0100 Subject: [PATCH 15/15] delete mix.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pepe Márquez Romero --- mix.lock | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 mix.lock diff --git a/mix.lock b/mix.lock deleted file mode 100644 index c5012b8..0000000 --- a/mix.lock +++ /dev/null @@ -1,22 +0,0 @@ -%{ - "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, - "ex_doc": {:hex, :ex_doc, "0.39.3", "519c6bc7e84a2918b737aec7ef48b96aa4698342927d080437f61395d361dcee", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "0590955cf7ad3b625780ee1c1ea627c28a78948c6c0a9b0322bd976a079996e1"}, - "finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"}, - "fine": {:hex, :fine, "0.1.4", "b19a89c1476c7c57afb5f9314aed5960b5bc95d5277de4cb5ee8e1d1616ce379", [:mix], [], "hexpm", "be3324cc454a42d80951cf6023b9954e9ff27c6daa255483b3e8d608670303f5"}, - "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, - "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, - "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, - "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, - "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, - "pythonx": {:hex, :pythonx, "0.4.7", "604a3a78377abdaa8739c561cb871c856b0e80d25fd057277839912017004af0", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.2", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "20d8b456df995e6ccd6d88dcf118ba80464194515f71a5c89aacdb824d235c52"}, - "req": {:hex, :req, "0.5.16", "99ba6a36b014458e52a8b9a0543bfa752cb0344b2a9d756651db1281d4ba4450", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "974a7a27982b9b791df84e8f6687d21483795882a7840e8309abdbe08bb06f09"}, - "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "uf2tool": {:hex, :uf2tool, "1.1.0", "7091931234ca5a256b66ea691983867d51229798622d083f85c1ad779798a734", [:rebar3], [], "hexpm", "1a7e5ca7ef3d19c7a0b0acf3db804b3188e0980884acffa13fd57d733507b73d"}, -}