Skip to content

Commit

Permalink
Merge pull request #439 from evnu/macos-bail-if-missing-config
Browse files Browse the repository at this point in the history
Abort compilation on macos if linker arguments are missing
  • Loading branch information
evnu authored Mar 18, 2022
2 parents 1c32256 + d5bd477 commit c2cff60
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 53 deletions.
94 changes: 56 additions & 38 deletions rustler_mix/lib/rustler/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ defmodule Rustler.Compiler do
|> make_features_flag(config.features)
|> make_target_flag(config.target)
|> make_build_mode_flag(config.mode)
|> make_platform_hacks(crate_full_path, :os.type())

ensure_platform_requirements!(crate_full_path, config, :os.type())

compile_result =
System.cmd(cmd, args,
Expand Down Expand Up @@ -69,46 +70,63 @@ defmodule Rustler.Compiler do
["rustup", "run", version, "cargo", "rustc"]
end

defp make_platform_hacks(args, crate_path, {:unix, :darwin}) do
root = Path.join([".cargo", "config"])
path = Path.join([crate_path, ".cargo", "config"])

if File.exists?(root) || File.exists?(path) do
args
else
IO.write([
"\n",
IO.ANSI.yellow(),
"""
Compiling on macOS requires special link args in order to compile
correctly.
Rustler is currently working around this issue in the compiler task.
This will be removed in v1.0.0 in favor of a user supplied .cargo/config
file.
To remove this warning, please create #{path}
with the following content:
[target.'cfg(target_os = "macos")']
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
See https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/executing_files.html
for more details.
""",
IO.ANSI.default_color(),
"\n"
])

args ++ ["--", "-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
defp ensure_platform_requirements!(crate_path, config, {:unix, :darwin}) do
# We attempt to find a .cargo/config upwards from the crate_path, which
# has a target configuration for macos. If any such config exists, we
# assume that the config correctly encodes the needed linker arguments.

workspace_root = config.metadata["workspace_root"]

components =
crate_path
|> Path.relative_to(workspace_root)
|> Path.split()

{potential_config_files, _} =
Enum.map_reduce(["" | components], workspace_root, fn component, path ->
path = Path.join(path, component)
file = Path.join([path, ".cargo", "config"])
{file, path}
end)

has_macos_target_os_configuration? =
potential_config_files
|> Enum.filter(&File.exists?/1)
|> Enum.reverse()
|> Stream.map(&Toml.decode_file!/1)
|> Enum.find(&macos_target_configuration/1)

unless has_macos_target_os_configuration? do
raise """
Compiling on macOS requires special link args in order to compile
correctly.
To remove this error, please create .cargo/config
with the following content:
[target.'cfg(target_os = "macos")']
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
See https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/executing_files.html
for more details.
"""
end
end

defp make_platform_hacks(args, _, _), do: args
defp ensure_platform_requirements!(_, _, _), do: :ok

defp macos_target_configuration(toml) do
toml
|> Map.get("target", [])
|> Enum.filter(fn {key, _} ->
String.match?(key, ~r/(.*macos.*)|(.*darwin.*)/)
end)
|> Map.new()
end

defp make_no_default_features_flag(args, true), do: args
defp make_no_default_features_flag(args, false), do: args ++ ["--no-default-features"]
Expand Down
33 changes: 18 additions & 15 deletions rustler_mix/lib/rustler/compiler/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ defmodule Rustler.Compiler.Config do
priv_dir: "",
skip_compilation?: false,
target: nil,
target_dir: ""
target_dir: "",
metadata: nil

alias Rustler.Compiler.Config

Expand Down Expand Up @@ -58,25 +59,25 @@ defmodule Rustler.Compiler.Config do
end

defp build(opts) do
crate = Keyword.fetch!(opts, :crate)

resources =
opts =
if opts[:skip_compilation?] do
[]
opts
else
crate = Keyword.fetch!(opts, :crate)
crate_path = Keyword.fetch!(opts, :path)

metadata = metadata!(crate_path)
resources = external_resources(crate_path, crate, metadata)

opts
|> Keyword.get(:path)
|> external_resources(crate)
|> Keyword.put(:metadata, metadata)
|> Keyword.put(:external_resources, resources)
end

opts = Keyword.put(opts, :external_resources, resources)

struct!(Config, opts)
end

defp external_resources(crate_path, crate) do
crate_str = to_string(crate)

defp metadata!(crate_path) do
metadata =
case System.cmd("cargo", ~w(metadata --format-version=1), cd: crate_path) do
{metadata, 0} ->
Expand All @@ -86,10 +87,12 @@ defmodule Rustler.Compiler.Config do
raise "calling `cargo metadata` failed.\n" <> output
end

json = Jason.decode!(metadata)

packages = json["packages"]
Jason.decode!(metadata)
end

defp external_resources(crate_path, crate, metadata) do
crate_str = to_string(crate)
packages = Map.fetch!(metadata, "packages")
crate_spec = get_spec(packages, crate_str)

packages
Expand Down

0 comments on commit c2cff60

Please sign in to comment.