-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add gleam support to Mix #14262
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
base: main
Are you sure you want to change the base?
Add gleam support to Mix #14262
Changes from all commits
c5bd684
3fcf897
85ba7b2
96a1c0b
7e21ba0
e3c18a5
3ca3c6f
b142e94
ec9e597
08b2a54
26203af
5ff6bc9
d7f4558
449c45e
c7693b2
8d800f8
7ffa8c8
46aa2d6
7e51726
47f3b66
2f0efe4
8f75b1b
00f852d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||
| # SPDX-License-Identifier: Apache-2.0 | ||||||
| # SPDX-FileCopyrightText: 2021 The Elixir Team | ||||||
|
|
||||||
| defmodule Mix.Gleam do | ||||||
| # Version that introduced `gleam export package-information` command | ||||||
| @required_gleam_version ">= 1.10.0" | ||||||
|
|
||||||
| def load_config(dir) do | ||||||
| File.cd!(dir, fn -> | ||||||
| gleam!(~W(export package-information --out /dev/stdout)) | ||||||
| |> JSON.decode!() | ||||||
| |> Map.fetch!("gleam.toml") | ||||||
| |> parse_config() | ||||||
| end) | ||||||
| end | ||||||
|
|
||||||
| def parse_config(json) do | ||||||
| deps = | ||||||
| Map.get(json, "dependencies", %{}) | ||||||
| |> Enum.map(&parse_dep/1) | ||||||
|
|
||||||
| dev_deps = | ||||||
| Map.get(json, "dev-dependencies", %{}) | ||||||
| |> Enum.map(&parse_dep(&1, only: [:dev, :test])) | ||||||
|
|
||||||
| %{ | ||||||
| name: Map.fetch!(json, "name"), | ||||||
| version: Map.fetch!(json, "version"), | ||||||
| deps: deps ++ dev_deps | ||||||
| } | ||||||
| |> maybe_gleam_version(json) | ||||||
| |> maybe_erlang_opts(json["erlang"]) | ||||||
| rescue | ||||||
| KeyError -> | ||||||
| Mix.raise("Command \"gleam export package-information\" unexpected format: \n" <> json) | ||||||
| end | ||||||
|
|
||||||
| defp parse_dep({dep, requirement}, opts \\ []) do | ||||||
| dep = String.to_atom(dep) | ||||||
|
|
||||||
| spec = | ||||||
| case requirement do | ||||||
| %{"version" => version} -> | ||||||
| {dep, version, opts} | ||||||
|
|
||||||
| %{"path" => path} -> | ||||||
| {dep, Keyword.merge(opts, path: Path.expand(path))} | ||||||
|
|
||||||
| %{"git" => git, "ref" => ref} -> | ||||||
| {dep, git: git, ref: ref} | ||||||
|
|
||||||
| _ -> | ||||||
| Mix.raise("Gleam package #{dep} has unsupported requirement: #{inspect(requirement)}") | ||||||
| end | ||||||
|
|
||||||
| case spec do | ||||||
| {dep, version, []} -> {dep, version} | ||||||
| spec -> spec | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| defp maybe_gleam_version(config, json) do | ||||||
| case json["gleam"] do | ||||||
| nil -> config | ||||||
| version -> Map.put(config, :gleam, version) | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| defp maybe_erlang_opts(config, nil), do: config | ||||||
|
|
||||||
| defp maybe_erlang_opts(config, opts) do | ||||||
| application = | ||||||
| opts | ||||||
| |> Enum.reject(fn {_, value} -> value == nil end) | ||||||
| |> Enum.map(fn | ||||||
| {"application_start_module", module} when is_binary(module) -> | ||||||
| {:mod, {String.to_atom(module), []}} | ||||||
|
|
||||||
| {"extra_applications", applications} when is_list(applications) -> | ||||||
| {:extra_applications, Enum.map(applications, &String.to_atom/1)} | ||||||
|
|
||||||
| {key, value} -> | ||||||
| IO.warn("Gleam [erlang] option not supported\n #{key}: #{inspect(value)}") | ||||||
| end) | ||||||
|
|
||||||
| Map.put(config, :application, application) | ||||||
| end | ||||||
|
|
||||||
| def require!() do | ||||||
| available_version() | ||||||
| |> Version.match?(@required_gleam_version) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This requirement is not being enforced. It could return I think a test should be added to cover this. |
||||||
| end | ||||||
|
|
||||||
| defp available_version do | ||||||
| case gleam!(["--version"]) do | ||||||
| "gleam " <> version -> Version.parse!(version) |> Version.to_string() | ||||||
| output -> Mix.raise("Command \"gleam --version\" unexpected format: #{output}") | ||||||
| end | ||||||
| rescue | ||||||
| e in Version.InvalidVersionError -> | ||||||
| Mix.raise("Command \"gleam --version\" invalid version format: #{e.version}") | ||||||
| end | ||||||
|
|
||||||
| defp gleam!(args) do | ||||||
| System.cmd("gleam", args) | ||||||
| catch | ||||||
| :error, :enoent -> | ||||||
| Mix.raise( | ||||||
| "The \"gleam\" executable is not available in your PATH. " <> | ||||||
| "Please install it, as one of your dependencies requires it. " | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Removed trailing white-space and if I am not mistaken, in Elixir we don't end error messages with a period (even if they are two sentences like in this case), but I could be wrong. |
||||||
| ) | ||||||
| else | ||||||
| {response, 0} -> | ||||||
| String.trim(response) | ||||||
|
|
||||||
| {response, _} when is_binary(response) -> | ||||||
| Mix.raise("Command \"gleam #{Enum.join(args, " ")}\" failed with reason: #{response}") | ||||||
|
|
||||||
| {_, _} -> | ||||||
| Mix.raise("Command \"gleam #{Enum.join(args, " ")}\" failed") | ||||||
| end | ||||||
| end | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler is not catching this.
jsonis a map, but we are appending it to a string.A tests could help catch bugs like this. Adding a guard to the function detects it though.