From a959b0e3218f3158c12d4037ad6aab678bc606e5 Mon Sep 17 00:00:00 2001 From: MuNeNiCK Date: Wed, 26 Aug 2026 13:31:42 +0900 Subject: [PATCH 1/2] fix: support nested Cargo workspace packages --- lib/rekindle/build.ex | 16 +------- lib/rekindle/cargo.ex | 22 +++++----- test/fixtures/cargo_workspace/Cargo.toml | 3 ++ .../crates/fixture_ui/Cargo.toml | 18 ++++++++ .../crates/fixture_ui/src/bin/desktop.rs | 1 + .../crates/fixture_ui/src/bin/web.rs | 1 + test/rekindle/build_test.exs | 18 +------- test/rekindle/cargo_test.exs | 41 +++++++++++++++++++ 8 files changed, 76 insertions(+), 44 deletions(-) create mode 100644 test/fixtures/cargo_workspace/Cargo.toml create mode 100644 test/fixtures/cargo_workspace/crates/fixture_ui/Cargo.toml create mode 100644 test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/desktop.rs create mode 100644 test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/web.rs diff --git a/lib/rekindle/build.ex b/lib/rekindle/build.ex index 7cc0fb7..4cae141 100644 --- a/lib/rekindle/build.ex +++ b/lib/rekindle/build.ex @@ -20,8 +20,7 @@ defmodule Rekindle.Build do with :ok <- validate_target(target), :ok <- validate_profile(profile), - {:ok, target_config} <- enabled_target(project, target), - :ok <- entry_exists(project, target_config) do + {:ok, target_config} <- enabled_target(project, target) do dispatch(project, target_config, profile, options) end end @@ -50,19 +49,6 @@ defmodule Rekindle.Build do end end - defp entry_exists(project, target) do - path = Path.join(project.root, target.entry) - - if File.regular?(path) do - :ok - else - error( - :missing_entry, - "#{target.name} is enabled but #{target.entry} is missing; restore the entry or disable the target" - ) - end - end - defp dispatch(project, %{name: :web} = target, profile, options), do: Rekindle.Web.Builder.build(project, target, profile, options) diff --git a/lib/rekindle/cargo.ex b/lib/rekindle/cargo.ex index 11cbf6a..1a157e1 100644 --- a/lib/rekindle/cargo.ex +++ b/lib/rekindle/cargo.ex @@ -43,12 +43,12 @@ defmodule Rekindle.Cargo do @spec resolve(Metadata.t(), Rekindle.Config.t(), Target.t()) :: {:ok, Metadata.package(), String.t()} | {:error, Error.t()} - def resolve(metadata, project, target) do + def resolve(metadata, _project, target) do workspace_packages = Enum.filter(metadata.packages, &MapSet.member?(metadata.workspace_members, &1.id)) with {:ok, package} <- package(workspace_packages, target.package), - {:ok, binary} <- binary(package, project, target) do + {:ok, binary} <- binary(package, target) do {:ok, package, binary} end end @@ -81,17 +81,12 @@ defmodule Rekindle.Cargo do end end - defp binary(package, project, target) do - expected_entry = - project.client_root - |> Path.join(Path.relative_to(target.entry, "client")) - |> Path.expand() + defp binary(package, target) do + name = target.binary || Atom.to_string(target.name) candidates = Enum.filter(package.targets, fn candidate -> - "bin" in candidate["kind"] and - (is_nil(target.binary) or candidate["name"] == target.binary) and - Path.expand(candidate["src_path"]) == expected_entry + "bin" in candidate["kind"] and candidate["name"] == name end) case candidates do @@ -101,11 +96,14 @@ defmodule Rekindle.Cargo do [] -> error( :binary_not_found, - "Cargo package #{inspect(package.name)} has no binary for #{target.entry}" + "Cargo package #{inspect(package.name)} has no binary named #{inspect(name)}" ) _ -> - error(:ambiguous_binary, "Cargo metadata returned multiple binaries for #{target.entry}") + error( + :ambiguous_binary, + "Cargo metadata returned multiple binaries named #{inspect(name)}" + ) end end diff --git a/test/fixtures/cargo_workspace/Cargo.toml b/test/fixtures/cargo_workspace/Cargo.toml new file mode 100644 index 0000000..7555590 --- /dev/null +++ b/test/fixtures/cargo_workspace/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["crates/fixture_ui"] +resolver = "2" diff --git a/test/fixtures/cargo_workspace/crates/fixture_ui/Cargo.toml b/test/fixtures/cargo_workspace/crates/fixture_ui/Cargo.toml new file mode 100644 index 0000000..a33a2ea --- /dev/null +++ b/test/fixtures/cargo_workspace/crates/fixture_ui/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fixture_ui" +version = "0.1.0" +edition = "2024" + +[features] +desktop = [] +web = [] + +[[bin]] +name = "desktop" +path = "src/bin/desktop.rs" +required-features = ["desktop"] + +[[bin]] +name = "web" +path = "src/bin/web.rs" +required-features = ["web"] diff --git a/test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/desktop.rs b/test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/desktop.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/desktop.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/web.rs b/test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/web.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/test/fixtures/cargo_workspace/crates/fixture_ui/src/bin/web.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/test/rekindle/build_test.exs b/test/rekindle/build_test.exs index 8c65ca9..040fd92 100644 --- a/test/rekindle/build_test.exs +++ b/test/rekindle/build_test.exs @@ -31,7 +31,7 @@ defmodule Rekindle.BuildTest do Rekindle.build(:web) end - test "rejects unknown and disabled targets before accessing an entry", %{root: root} do + test "rejects unknown and disabled targets before invoking Cargo", %{root: root} do Application.put_env(:rekindle_build_test, Rekindle, plugin: Rekindle.Plugin.GPUI, targets: [desktop: [features: []]] @@ -44,22 +44,6 @@ defmodule Rekindle.BuildTest do Rekindle.build(:web, otp_app: :rekindle_build_test, project_root: root) end - test "rejects missing canonical entries before dispatch", %{root: root} do - Application.put_env(:rekindle_build_test, Rekindle, - plugin: Rekindle.Plugin.GPUI, - targets: [desktop: [features: []]] - ) - - assert {:error, %Rekindle.Build.Error{kind: :missing_entry, message: message}} = - Rekindle.build(:desktop, - otp_app: :rekindle_build_test, - project_root: root, - profile: :release - ) - - assert message =~ "client/src/bin/desktop.rs" - end - test "returns a Cargo artifact without starting it", %{root: root} do File.cp_r!("test/fixtures/cargo_project", Path.join(root, "client")) diff --git a/test/rekindle/cargo_test.exs b/test/rekindle/cargo_test.exs index 02a3e45..a8169fc 100644 --- a/test/rekindle/cargo_test.exs +++ b/test/rekindle/cargo_test.exs @@ -71,6 +71,17 @@ defmodule Rekindle.CargoTest do assert Path.basename(result.artifact) == "desktop" end + test "builds a binary from a nested workspace package", %{project: project} do + File.rm_rf!(project.client_root) + File.cp_r!("test/fixtures/cargo_workspace", project.client_root) + target = %{target(:desktop) | package: "fixture_ui", features: ["desktop"]} + + assert {:ok, result} = Cargo.build(project, target, :dev) + assert result.package == "fixture_ui" + assert result.binary == "desktop" + assert File.regular?(result.artifact) + end + test "discovers the Web Wasm artifact from Cargo messages", %{project: project} do target = target(:web) cargo = rustup_path("cargo") @@ -101,6 +112,36 @@ defmodule Rekindle.CargoTest do Cargo.build(project, target, :dev) end + test "resolves a binary from a nested workspace package", %{project: project} do + package = cargo_package(project, "fixture_ui", "fixture_ui 0.1.0") + + nested = %{ + package + | manifest_path: Path.join(project.client_root, "crates/fixture_ui/Cargo.toml"), + targets: + Enum.map(package.targets, fn target -> + %{ + target + | "src_path" => + Path.join( + project.client_root, + "crates/fixture_ui/src/bin/#{target["name"]}.rs" + ) + } + end) + } + + metadata = %Cargo.Metadata{ + packages: [nested], + workspace_members: MapSet.new([nested.id]), + target_directory: Path.join(project.client_root, "target") + } + + configured = %{target(:web) | package: "fixture_ui"} + + assert {:ok, ^nested, "web"} = Cargo.resolve(metadata, project, configured) + end + test "passes the configured Cargo selection and build arguments exactly", %{project: project} do {cargo, arguments_file, _started_file, metadata_cwd_file, build_cwd_file} = fake_cargo(project, :artifact) From 622eb6016da560ce0032b8a3521e3f481d354ddf Mon Sep 17 00:00:00 2001 From: MuNeNiCK Date: Wed, 26 Aug 2026 13:31:42 +0900 Subject: [PATCH 2/2] chore: prepare Rekindle 0.1.12 --- CHANGELOG.md | 5 +++++ mix.exs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c31e625..2fe2254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +## 0.1.12 - 2026-08-26 + +- Resolve Web and desktop binaries from the selected Cargo workspace package + instead of requiring entry files at the client root. + ## 0.1.11 - 2026-08-26 - Make the one-shot Igniter quick start complete in non-interactive diff --git a/mix.exs b/mix.exs index 1e9f5c3..a924891 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Rekindle.MixProject do use Mix.Project - @version "0.1.11" + @version "0.1.12" @source_url "https://github.com/refmdio/rekindle" @docs_url "https://rekindle.hexdocs.pm"