Packages
Elixir library for obtaining Erlang/OTP version in full resolution (thus beyond System.otp_release/0) and, optionally, in Version-compatible format.
Current section
Files
Jump to
Current section
Files
lib/otp_version.ex
defmodule OTPVersion do
@moduledoc """
Provides access to Erlang/OTP version beyond `System.otp_release/0` (major only).
## Example:
iex> Version.match?(OTPVersion.otp_version(:semantic_versioning_scheme), ">= 17.0.0")
true
"""
@typedoc """
The version scheme to be passed to `otp_version/1`.
* `:otp_version_scheme` - full resolution version information; conforms to
[Erlang - Versions - 5.3 Version Scheme](https://erlang.org/doc/system_principles/versions.html#version-scheme)
* `:semantic_versioning_scheme` - forces Erlang/OTP version, which does not conform to SemVer,
into the format outlined in [SemVer 2.0 schema](https://semver.org/)
(i.e., `MAJOR.MINOR.PATCH`), therefore compatible with `Version`, by padding with
zeros after `MINOR` or truncating after `PATCH`
* `:major_only` - equivalent to `System.otp_release/0`
"""
@type version_scheme :: :otp_version_scheme | :semantic_versioning_scheme | :major_only
@doc false
defmacro system_otp_release do
if Version.match?(System.build_info().version, ">= 1.3.0") do
quote do
System.otp_release()
end
else
quote do
:erlang.list_to_binary(:erlang.system_info(:otp_release))
end
end
end
@doc """
Returns the Erlang/OTP version in the specified version scheme.
If none is specified, defaults to OTP version scheme.
"""
@spec otp_version(version_scheme) :: String.t()
def otp_version(version_scheme \\ :otp_version_scheme) do
case version_scheme do
:major_only ->
system_otp_release()
:otp_version_scheme ->
get_otp_version()
:semantic_versioning_scheme ->
normalize_to_semantic_versioning_scheme(otp_version(:otp_version_scheme))
end
end
# Based on https://github.com/hexpm/hex/blob/92f31922/lib/hex/utils.ex#L202
@spec get_otp_version() :: String.t()
defp get_otp_version do
major = system_otp_release()
try do
{:ok, contents} = File.read(otp_version_file(major))
String.split(contents, "\n", trim: true)
else
[full] -> full
_ -> major
catch
:error, _ -> major
end
end
@spec trim(String.t()) :: String.t()
defmacrop trim(string) do
if Version.match?(System.build_info().version, ">= 1.3.0") do
quote do
String.trim(unquote(string))
end
else
quote do
String.strip(unquote(string))
end
end
end
# Based on https://github.com/mirego/elixir-boilerplate/blob/2e5170a2/lib/mix/tasks/erlang.check_version.ex#L42
@doc false
@spec normalize_to_semantic_versioning_scheme(String.t()) :: String.t()
def normalize_to_semantic_versioning_scheme(version) do
version
|> trim()
|> String.split(".")
|> case do
[major] -> "#{major}.0.0"
[major, minor] -> "#{major}.#{minor}.0"
[major, minor, patch | _] -> "#{major}.#{minor}.#{patch}"
end
end
@doc false
@spec otp_version_file(String.t()) :: String.t()
def otp_version_file(major) when is_binary(major) do
Path.join([:code.root_dir(), "releases", major, "OTP_VERSION"])
end
end