Skip to content
This repository was archived by the owner on Sep 18, 2024. It is now read-only.
This repository was archived by the owner on Sep 18, 2024. It is now read-only.

[Knowledge share] Cross-compiling to Windows from Linux #134

@KoviRobi

Description

@KoviRobi

Hi,

EDIT:

Ah I saw in another issue you are pointing people to https://github.com/burrito-elixir/burrito which seems to support this out of the box

After looking at Burrito for a bit, I can confirm it is taking the same approach, plus using zig instead of C, which allows it to have an easier time at cross-compiling.

Just sharing my workaround for getting a bakeware executable that's compiled on Linux to run on Windows (for me it has CI benefits, doing it this way). I've also cached the otp_win64_24.3.4.5.exe binary (added to my git repo via LFS). Basically it just replaces the ERTS and other erlang libraries to the Windows one in the release.

Also of course, I have CC ="/path/to/x86_64-w64-mingw32-gcc" to compile the bakeware executable to .exe

I didn't know where best to put it, this is a workaround rather than a full solution, but I think it is a good enough workaround that it's worth sharing.

  defp otp_full_version() do
    otp_major = :erlang.system_info(:otp_release)
    File.read!(Path.join([:code.root_dir(), "releases", otp_major, "OTP_VERSION"]))
    |> String.trim()
  end

  defp extract_cross_erlang() do
    cached_version = "24.3.4.5"
    erts_version = :erlang.system_info(:version)
    otp_version = otp_full_version()
    if otp_version != cached_version do
      raise """
        Wrong erlang version, download the one matching your version:
        OTP: #{otp_version}, ERTS: #{erts_version}
        from e.g. this was from
        https://www.erlang.org/patches/otp-#{cached_version}, so for you it's
        probably https://www.erlang.org/patches/otp-#{otp_version}
      """
    end

    if not File.exists?("cross-erlang") do
      case System.get_env("MIX_TARGET") do
        "win64" -> System.cmd("7z", ["x", "otp_win64_#{cached_version}.exe", "-ocross-erlang"])
      end
    end
  end

  defp patch_for_cross_compile(release) do
    if System.get_env("MIX_TARGET") do
      extract_cross_erlang()

      cross_release = %{release |
        erts_source: './cross-erlang/erts-12.3.2.5',
        applications:
          for {app, cfg} <- release.applications, into: %{} do
            path = cfg[:path] |> to_string
            root_dir = :code.root_dir() |> to_string()

            if String.starts_with?(path, root_dir) do
              {app,
                put_in(
                  cfg[:path],
                  String.replace(path, root_dir, "./cross-erlang")
                  |> to_charlist)}
            else
              {app, cfg}
            end
          end}

      if System.get_env("MIX_RELEASE_DEBUG") do
        File.write!("release", inspect(release, pretty: true, limit: :infinity))
        File.write!("cross_release", inspect(cross_release, pretty: true, limit: :infinity))
      end

      cross_release
    else
      release
    end
  end

  defp release do
    [
      overwrite: true,
      cookie: "#{@app}_cookie",
      quiet: true,
      steps: [
        &patch_for_cross_compile/1,
        :assemble,
        &Bakeware.assemble/1
      ],
      strip_beams: Mix.env() == :prod
    ]
  end

And a small patch on top of Elixir I use as a workaround to not having got a matching Elixir 1.12.3 that works with OTP 24 (Elixir doesn't have any DLLs anyway, just BEAM files, it was just missing the Windows .bat files)

diff --git a/Makefile b/Makefile
index 8f5d093..058f81f 100644
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ install: compile
 		$(INSTALL_DATA) $$dir/ebin/* "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/$$dir/ebin"; \
 	done
 	$(Q) $(INSTALL_DIR) "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/bin"
-	$(Q) $(INSTALL_PROGRAM) $(filter-out %.ps1, $(filter-out %.bat, $(wildcard bin/*))) "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/bin"
+	$(Q) $(INSTALL_PROGRAM) $(wildcard bin/*) "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/bin"
 	$(Q) $(INSTALL_DIR) "$(DESTDIR)$(PREFIX)/$(BINDIR)"
 	$(Q) for file in "$(DESTDIR)$(PREFIX)"/$(LIBDIR)/elixir/bin/*; do \
 		ln -sf "../$(LIBDIR)/elixir/bin/$${file##*/}" "$(DESTDIR)$(PREFIX)/$(BINDIR)/"; \

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions