Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix msvc compilation #132

Merged
merged 12 commits into from
Sep 9, 2024
10 changes: 9 additions & 1 deletion lib/bundlex/build_script.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ defmodule Bundlex.BuildScript do

def run(%__MODULE__{commands: commands}, platform) do
family = Platform.family(platform)
cmd = commands |> join_commands(family)
cmd = commands |> join_commands_run(family)

case cmd |> Mix.shell().cmd() do
0 -> :ok
Expand All @@ -55,6 +55,14 @@ defmodule Bundlex.BuildScript do
end
end

# TODO: this is obviously awkward but I'm very tired.
# should the toolchains define these?
defp join_commands_run(commands, :windows) do
Enum.join(commands, " && ")
end

defp join_commands_run(commands, family), do: join_commands(commands, family)

defp join_commands(commands, :windows) do
Enum.join(commands, "\r\n") <> "\r\n"
end
Expand Down
9 changes: 0 additions & 9 deletions lib/bundlex/helper/path_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@ defmodule Bundlex.Helper.PathHelper do
@moduledoc false
# Module containing helper functions that ease traversing directories.

@doc """
Tries to find a path that matches given pattern that has the biggest
version number if it is expected to be a suffix.
"""
@spec latest_wildcard(String.t()) :: nil | String.t()
def latest_wildcard(pattern) do
pattern |> Path.wildcard() |> Enum.max(fn -> nil end)
end

@doc """
Fixes slashes in the given path to match convention used on current
operating system.
Expand Down
63 changes: 16 additions & 47 deletions lib/bundlex/toolchain/visual_studio.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ defmodule Bundlex.Toolchain.VisualStudio do
alias Bundlex.Native
alias Bundlex.Output

@directory_wildcard_x64 "c:\\Program Files (x86)\\Microsoft Visual Studio *"
@directory_wildcard_x86 "c:\\Program Files\\Microsoft Visual Studio *"
@directory_env "VISUAL_STUDIO_ROOT"
@program_files System.get_env("ProgramFiles(x86)") |> Path.expand()
@directory_root Path.join([@program_files, "Microsoft Visual Studio"])

# TODO: These should also include the ability to set the target architecture.
@impl true
def before_all!(:windows32) do
[run_vcvarsall("x86")]
Expand Down Expand Up @@ -58,16 +58,16 @@ defmodule Bundlex.Toolchain.VisualStudio do
dir_part = "\"#{unquoted_dir_part}\""

[
"if EXIST #{dir_part} rmdir /S /Q #{dir_part}",
"mkdir #{dir_part}",
"cl /LD #{includes_part} #{sources_part} #{libs_part} /link /DLL /OUT:\"#{Toolchain.output_path(native.app, native.name, :nif)}.dll\""
"(if exist #{dir_part} rmdir /S /Q #{dir_part})",
"(mkdir #{dir_part})",
~s[(cl /LD #{includes_part} #{sources_part} #{libs_part} /link /DLL /OUT:"#{Toolchain.output_path(native.app, native.name, :nif) |> PathHelper.fix_slashes}.dll")]
]
end

# Runs vcvarsall.bat script
defp run_vcvarsall(vcvarsall_arg) do
vcvarsall_path =
determine_visual_studio_root()
@directory_root
|> build_vcvarsall_path()

case File.exists?(vcvarsall_path) do
Expand All @@ -77,50 +77,19 @@ defmodule Bundlex.Toolchain.VisualStudio do
)

true ->
"if not defined VCINSTALLDIR call \"#{vcvarsall_path}\" #{vcvarsall_arg}"
~s/(if not defined VCINSTALLDIR call "#{vcvarsall_path}" #{vcvarsall_arg})/
end
end

# Determines root directory of the Visual Studio.
defp determine_visual_studio_root() do
determine_visual_studio_root(System.get_env(@directory_env))
end

# Determines root directory of the Visual Studio.
# Case when we don't have a root path passed via an environment variable.
defp determine_visual_studio_root(nil) do
visual_studio_path()
|> determine_visual_studio_root_with_wildcard()
end

# Determines root directory of the Visual Studio.
# Case when we have a root path passed via an environment variable.
defp determine_visual_studio_root(directory) do
directory
end

defp determine_visual_studio_root_with_wildcard(wildcard) do
case PathHelper.latest_wildcard(wildcard) do
nil ->
Output.raise(
"Unable to find Visual Studio root directory. Please ensure that it is either located in \"#{wildcard}\" or #{@directory_env} environment variable pointing to its root is set."
)

directory ->
directory
end
end

# Builds path to the vcvarsall.bat script that can be used to set environment
# variables necessary to use Visual Studio compilers.
defp build_vcvarsall_path(root) do
Path.join([root, "VC", "vcvarsall.bat"])
end

defp visual_studio_path() do
case :erlang.system_info(:wordsize) do
4 -> @directory_wildcard_x86
_word_size -> @directory_wildcard_x64
vswhere = Path.join([root, "Installer", "vswhere.exe"])
vswhere_args = ["-property", "installationPath", "-latest"]
with true <- File.exists?(vswhere),
{maybe_installation_path, 0} <- System.cmd(vswhere, vswhere_args)
do
installation_path = String.trim(maybe_installation_path)
Path.join([installation_path, "VC", "Auxiliary", "Build", "vcvarsall.bat"])
|> PathHelper.fix_slashes()
end
end
end
Loading