查看源代码 Mix (Mix v1.16.2)

Mix 是一个构建工具,它提供了用于创建、编译和测试 Elixir 项目、管理其依赖项等等的任务。

Mix.Project

Mix 的基础是一个项目。项目可以通过在模块中使用 Mix.Project 来定义,通常放在名为 mix.exs 的文件中。

defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      app: :my_app,
      version: "1.0.0"
    ]
  end
end

有关 Mix 项目的详细文档,请参阅 Mix.Project 模块。

定义项目后,可以直接从命令行运行许多默认的 Mix 任务。

每个任务都有自己的选项,有时需要在 project/0 函数中定义特定的配置。可以使用 mix help 列出所有可用的任务,并使用 mix help NAME 显示特定任务的帮助信息。

从命令行调用 mix new my_project 是开始第一个项目的最佳方法。

Mix.Task

任务使 Mix 可扩展。

项目可以通过添加自己的任务来扩展 Mix 的行为。例如,将下面的任务添加到您的项目中,将使它对使用您项目的每个人可用。

defmodule Mix.Tasks.Hello do
  use Mix.Task

  def run(_) do
    Mix.shell().info("Hello world")
  end
end

现在可以使用 mix hello 调用该任务。

有关 Mix 任务的详细文档,请参阅 Mix.Task 行为。

依赖项

Mix 还管理您的依赖项,并与 Hex 包管理器 无缝集成。

为了使用依赖项,您需要在项目配置中添加 :deps 键。我们通常将依赖项列表提取到自己的函数中

defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      app: :my_app,
      version: "1.0.0",
      deps: deps()
    ]
  end

  defp deps do
    [
      {:ecto, "~> 2.0"},
      {:plug, github: "elixir-lang/plug"}
    ]
  end
end

您可以运行 mix help deps 以了解有关 Mix 中依赖项的更多信息。

环境

Mix 支持不同的环境。环境允许开发人员专门为不同的场景准备和组织他们的项目。默认情况下,Mix 提供三个环境

  • :dev - 默认环境
  • :test - mix test 运行的环境
  • :prod - 您的依赖项运行的环境

可以通过设置 MIX_ENV 环境变量从命令行更改环境,例如

$ MIX_ENV=prod mix run server.exs

您也可以指定某些依赖项仅在特定环境下可用

{:some_test_dependency, "~> 1.0", only: :test}

在通过命令行运行 Mix 时,可以通过 mix.exs 中的 def cli 函数配置默认环境或每个任务的首选环境。例如

def cli do
  [
    default_env: :local,
    preferred_envs: [docs: :docs]
  ]
end

可以通过 Mix.env/0 读取环境。

目标

除了环境,Mix 还支持目标。当项目需要编译到不同的架构,并且某些依赖项仅对其中一些架构可用时,目标非常有用。默认情况下,目标是 :host,但可以通过 MIX_TARGET 环境变量设置。

在通过命令行运行 Mix 时,可以通过 mix.exs 中的 def cli 函数配置默认目标或每个任务的首选目标。例如

def cli do
  [
    default_target: :local,
    preferred_targets: [docs: :docs]
  ]
end

可以通过 Mix.target/0 读取目标。

配置

Mix 允许您配置应用程序和依赖项的应用程序环境。有关应用程序环境的更多信息,请参阅 Application 模块。在本节中,我们将重点介绍如何在两个不同的时间点配置它:构建时和运行时。

避免应用程序环境

不建议在库中使用应用程序环境。有关更多信息,请参阅 Elixir 的 库指南

构建时配置

无论何时调用 mix 命令,Mix 都会加载 config/config.exs 中的配置,如果该文件存在。 config/config.exs 文件本身通常会根据当前 MIX_ENV 导入其他配置,例如 config/dev.exsconfig/test.exsconfig/prod.exs,通过调用 Config.import_config/1

import Config
import_config "#{config_env()}.exs"

我们说 config/config.exs 和所有导入的文件是构建时配置,因为它们会在您编译代码时进行评估。换句话说,如果您的配置执行类似以下操作

import Config
config :my_app, :secret_key, System.fetch_env!("MY_APP_SECRET_KEY")

:my_app 下的 :secret_key 键将在您的代码编译之前在主机上计算。如果编译代码的机器无法访问用于运行代码的所有环境变量,这可能会成为问题,因为加载上面的配置将由于缺少环境变量而失败。此外,即使设置了环境变量,更改环境变量也需要通过调用 mix compile --force 重新编译您的应用程序(否则您的项目将无法启动)。幸运的是,Mix 还提供运行时配置,在这些情况下是首选,我们将在下面看到。

运行时配置

要在您的发行版中启用运行时配置,您只需要创建一个名为 config/runtime.exs 的文件。

import Config
config :my_app, :secret_key, System.fetch_env!("MY_APP_SECRET_KEY")

该文件在您的项目运行时执行。如果您使用 mix release 构建发行版,它也会在您的发行版启动时每次执行。

别名

别名是当前项目特定的快捷方式或任务。

Mix.Task 部分,我们定义了一个对使用我们的项目作为依赖项的每个人都可用的任务。如果我们想让该任务仅对我们的项目可用,该怎么办?只需定义一个别名。

defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      app: :my_app,
      version: "1.0.0",
      aliases: aliases()
    ]
  end

  defp aliases do
    [
      c: "compile",
      hello: &hello/1,
      paid_task: &paid_task/1
    ]
  end

  defp hello(_) do
    Mix.shell().info("Hello world")
  end

  defp paid_task(_) do
    Mix.Task.run("paid.task", [
      "first_arg",
      "second_arg",
      "--license-key",
      System.fetch_env!("SOME_LICENSE_KEY")
    ])
  end
end

在上面的示例中,我们定义了三个别名。一个是 mix c,它是 mix compile 的快捷方式。另一个名为 mix hello,第三个名为 mix paid_task,它在自定义函数中执行代码以使用多个参数调用 paid.task 任务,包括从环境变量中提取的一个参数。

别名也可以是列表,指定要连续运行的多个任务。

[all: [&hello/1, "deps.get --only #{Mix.env()}", "compile"]]

在上面的示例中,我们定义了一个名为 mix all 的别名,它打印“Hello world”,然后获取特定于当前环境的依赖项,并编译项目。

别名也可以用于增强现有任务。假设您想增强 mix clean 以清理 Mix 不知道的另一个目录。

[clean: ["clean", &clean_extra/1]]

其中 &clean_extra/1 将是您 mix.exs 中具有额外清理逻辑的函数。

如果别名正在覆盖现有任务,则传递给别名的参数将转发给原始任务,以保留语义。否则,传递给别名的参数将追加到列表中最后一个任务的参数。

别名的另一个用例是运行 Elixir 脚本和 shell 命令,例如

# priv/hello1.exs
IO.puts("Hello One")

# priv/hello2.exs
IO.puts("Hello Two")

# priv/world.sh
#!/bin/sh
echo "world!"

# mix.exs
defp aliases do
  [
    some_alias: ["hex.info", "run priv/hello1.exs", "cmd priv/world.sh"]
  ]
end

在上面的示例中,我们创建了别名 some_alias,它将运行任务 mix hex.info,然后 mix run 运行 Elixir 脚本,然后 mix cmd 执行命令行 shell 脚本。这展示了别名与 Mix 任务结合使用时的强大功能。

别名的一个常见问题是尝试多次调用同一个任务。Mix 任务设计为只运行一次。这可以防止同一个任务多次执行。例如,如果有几个任务依赖于 mix compile,代码将只编译一次。

类似地,mix format 只能调用一次。因此,如果您有一个尝试多次调用 mix format 的别名,它将无法工作,除非使用 Mix.Task.reenable/1 明确重新启用它。

another_alias: [
  "format --check-formatted priv/hello1.exs",
  "cmd priv/world.sh",
  fn _ -> Mix.Task.reenable("format") end,
  "format --check-formatted priv/hello2.exs"
]

不过,一些任务会自动重新启用,因为它们预计会多次调用,例如:mix cmdmix domix xref 等等。

最后,在当前项目中定义的别名不会影响其依赖项,并且在依赖项中定义的别名无法从当前项目访问,伞形项目除外。当伞形项目本身没有定义该别名,并且没有与该名称匹配的任务时,伞形项目将运行其子项目的别名。

环境变量

可以使用多个环境变量来修改 Mix 的行为。

Mix 响应以下变量

  • MIX_ARCHIVES - 指定应安装存档的目录(默认:~/.mix/archives

  • MIX_BUILD_PATH - 设置项目的 Mix.Project.build_path/0 配置。此选项必须始终指向临时目录中的子目录。例如,不要使用“/tmp”或“_build”,而是使用“_build/PROD”或“/tmp/PROD”,如 Mix 所需。此环境变量主要由外部构建工具使用。对于您的 CI 服务器,您可能希望使用下面的 MIX_BUILD_ROOT

  • MIX_BUILD_ROOT - 设置应将构建工件写入的根目录。例如,"_build"。如果设置了 MIX_BUILD_PATH,则此选项将被忽略。

  • MIX_DEBUG - 输出有关每个任务的调试信息,然后再运行它。

  • MIX_DEPS_PATH - 设置当前项目的 Mix.Project.deps_path/0 配置(默认:deps

  • MIX_ENV - 指定要使用的环境。请参阅 环境

  • MIX_EXS - 更改 mix.exs 文件的完整路径

  • MIX_HOME - Mix 主目录的路径,存储 Mix 使用的配置文件和脚本(默认:~/.mix

  • MIX_INSTALL_DIR - (自 v1.12.0 起)指定 Mix.install/2 保留安装缓存的目录

  • MIX_PATH - 附加额外的代码路径

  • MIX_PROFILE - 一个逗号分隔的 Mix 任务列表,用于分析运行任务的进程在函数上花费的时间

  • MIX_QUIET - 不打印信息消息到终端

  • MIX_REBAR3 - rebar3 命令的路径,它覆盖了 Mix 安装的路径(默认:~/.mix/rebar3

  • MIX_TARGET - 指定要使用的目标。请参阅 目标

  • MIX_XDG - 要求 Mix 遵循 XDG 目录规范 来设置其主目录和配置文件。由于向后兼容性,此行为需要选择加入。 MIX_HOME 的优先级高于 MIX_XDG。如果未设置任何变量,将使用默认目录 ~/.mix

不打算保存值的環境變數(基本上充當旗標)應該設定為 1true,例如

$ MIX_DEBUG=1 mix compile

摘要

函数

返回 Mix 使用的默认编译器。

设置 Mix 调试模式。

如果 Mix 处于调试模式,则返回 true,否则返回 false

确保来自 Erlang/OTP 或 Elixir 的给定应用程序及其依赖项在路径中可用。

返回当前的 Mix 环境。

将当前 Mix 环境更改为 env

安装并启动依赖项。

返回当前 Mix.install/2 项目所在的目录。

返回当前节点是否调用了 Mix.install/2

本地存档或 escript 的路径。

引发格式良好的 Mix 错误,默认为退出状态 1

引发格式良好的 Mix 错误。

返回当前 shell。

设置当前 shell。

返回 Mix 目标。

将当前 Mix 目标更改为 target

函数

@spec compilers() :: [atom()]

返回 Mix 使用的默认编译器。

它可以在您的 mix.exs 中使用,以便在 Mix 中添加或追加新的编译器

def project do
  [compilers: Mix.compilers() ++ [:foo, :bar]]
end
@spec debug(boolean()) :: :ok

设置 Mix 调试模式。

@spec debug?() :: boolean()

如果 Mix 处于调试模式,则返回 true,否则返回 false

链接到此函数

ensure_application!(app)

查看源代码 (自 1.15.0 起)

确保来自 Erlang/OTP 或 Elixir 的给定应用程序及其依赖项在路径中可用。

一般来说,您应该在 mix.exs:extra_applications 部分列出 Erlang 应用程序依赖项。这只能由 Mix 任务使用,这些任务出于某些原因希望避免依赖 Erlang/Elixir。

此函数不会启动给定的应用程序。

@spec env() :: atom()

返回当前的 Mix 环境。

此函数不应在应用程序代码(与基础设施和构建代码(如 Mix 任务)相反)的运行时使用。Mix 是一个构建工具,在代码编译后可能不可用(例如,在发布中)。

为了区分程序行为取决于环境,建议使用 Application.get_env/3 通过应用程序环境。可以在配置文件中设置适当的配置,通常每个环境一个(有关更多信息,请参阅 Config 模块)。

@spec env(atom()) :: :ok

将当前 Mix 环境更改为 env

调用此函数时要小心,因为任何项目配置都不会重新加载。

此函数不应在应用程序代码的运行时使用(有关更多信息,请参阅 env/0)。

链接到此函数

install(deps, opts \\ [])

查看源代码 (自 1.12.0 起)

安装并启动依赖项。

给定的 deps 应该与常规 Mix 项目中定义的格式相同。有关更多信息,请参阅 mix help deps。作为快捷方式,可以将原子作为依赖项来表示最新版本。换句话说,指定 :decimal 等同于 {:decimal, ">= 0.0.0"}

每次成功安装后,都会缓存一组给定的依赖项,因此启动另一个 VM 并使用相同的依赖项调用 Mix.install/2 将避免不必要的下载和编译。可以使用 MIX_INSTALL_DIR 环境变量控制缓存目录的位置。

此函数只能在 Mix 项目之外调用,并且只能在给定 VM 中使用相同的依赖项调用。

选项

  • :force - 如果为 true,则使用空的安装缓存运行。当您要更新依赖项或安装进入不一致状态时,这很有用。要使用此选项,您还可以设置 MIX_INSTALL_FORCE 环境变量。(默认:false

  • :verbose - 如果为 true,则打印额外的调试信息(默认:false

  • :consolidate_protocols - 如果为 true,则通过 mix compile.protocols 任务运行协议合并(默认:true

  • :elixir - 如果设置,则确保当前 Elixir 版本与给定的版本要求匹配(默认:nil

  • :system_env (自 v1.13.0 起) - 系统环境变量名称的列表或映射,以及各自的值作为二进制文件。系统环境成为 Mix.install/2 缓存的一部分,因此不同的配置将导致不同的应用程序

  • :config (自 v1.13.0 起) - 编译时配置的关键字列表。配置是 Mix.install/2 缓存的一部分,因此不同的配置将导致不同的应用程序。出于这个原因,您希望通过此选项最小化设置的配置数量。使用 Application.put_all_env/2 设置其他运行时配置。

  • :config_path (自 v1.14.0 起) - 指向配置文件的路径。如果给定路径的同一目录中存在 runtime.exs 文件,也会加载它。

  • :lockfile (自 v1.14.0 起) - 指向用作依赖项解析基础的锁文件的路径。

  • :start_applications (自 v1.15.3 起) - 如果为 true,则确保在安装后启动已安装的应用程序及其依赖项(默认:true

示例

安装 :decimal:jason

Mix.install([
  :decimal,
  {:jason, "~> 1.0"}
])

安装 :nx:exla,并配置底层应用程序和环境变量

Mix.install(
  [:nx, :exla],
  config: [
    nx: [default_backend: EXLA]
  ],
  system_env: [
    XLA_TARGET: "cuda111"
  ]
)

将 Mix 项目安装为路径依赖项,以及其配置和依赖项

# $ git clone https://github.com/hexpm/hexpm /tmp/hexpm
# $ cd /tmp/hexpm && mix setup

Mix.install(
  [
    {:hexpm, path: "/tmp/hexpm", env: :dev},
  ],
  config_path: "/tmp/hexpm/config/config.exs",
  lockfile: "/tmp/hexpm/mix.lock"
)

Hexpm.Repo.query!("SELECT COUNT(1) from packages")
#=> ...

上面的示例可以通过将应用程序名称作为原子传递给 :config_path:lockfile 来简化

Mix.install(
  [
    {:hexpm, path: "/tmp/hexpm", env: :dev},
  ],
  config_path: :hexpm,
  lockfile: :hexpm
)

限制

Mix.install/2 有一个限制,这实际上是 Elixir 的行为。如果您正在安装定义结构或宏的依赖项,则不能在安装调用后立即使用结构或宏。例如,这将无法正常工作

Mix.install([:decimal])
%Decimal{} = Decimal.new(42)

这是因为 Elixir 首先扩展所有结构和所有宏,然后执行代码。这意味着,当 Elixir 尝试扩展 %Decimal{} 结构时,依赖项尚未安装。

幸运的是,这有一个简单的解决方案,即将代码移动到模块内部

Mix.install([:decimal])

defmodule Script do
  def run do
    %Decimal{} = Decimal.new(42)
  end
end

Script.run()

defmodule 内部的内容只会在 Mix.install/2 运行后扩展和执行,这意味着任何结构、宏和导入都将被正确处理。

环境变量

MIX_INSTALL_DIR 环境变量配置缓存所有 Mix.install/2 的目录。

MIX_INSTALL_FORCE 自 Elixir v1.13.0 起可用,它强制 Mix.install/2 丢弃当前安装的任何先前缓存的条目。

MIX_INSTALL_RESTORE_PROJECT_DIR 环境变量可能自 Elixir v1.16.2 起指定。它应该指向之前的安装目录,可以通过 Mix.install_project_dir/0(在调用 Mix.install/2 后)获得。使用恢复目录可以加快安装速度,因为匹配的依赖项不需要重新获取也不需要重新编译。如果启用了 :force,则忽略此环境变量。

链接到此函数

install_project_dir()

查看源代码 (自 1.16.2 起)
@spec install_project_dir() :: Path.t() | nil

返回当前 Mix.install/2 项目所在的目录。

链接到此函数

installed?()

查看源代码 (自 1.13.0 起)

返回当前节点是否调用了 Mix.install/2

链接到此函数

path_for(atom)

查看源代码 (自 1.10.0 起)
@spec path_for(:archives | :escripts) :: String.t()

本地存档或 escript 的路径。

@spec raise(binary()) :: no_return()

引发格式良好的 Mix 错误,默认为退出状态 1

链接到此函数

raise(message, opts)

查看源代码 (自 1.12.3 起)
@spec raise(binary(), [{:exit_status, non_neg_integer()}]) :: no_return()

引发格式良好的 Mix 错误。

选项

  • :exit_status - 定义退出状态,默认为 1
@spec shell() :: module()

返回当前 shell。

shell/0 可用作当前 shell 的包装器。它包含用于向用户请求信息、向 shell 打印等等的便利功能。Mix shell 可交换(请参阅 shell/1),允许开发人员使用测试 shell,该 shell 仅将消息发送到当前进程,而不是执行 IO(请参阅 Mix.Shell.Process)。

默认情况下,这将返回 Mix.Shell.IO

示例

Mix.shell().info("Preparing to do something dangerous...")

if Mix.shell().yes?("Are you sure?") do
  # do something dangerous
end
@spec shell(module()) :: :ok

设置当前 shell。

作为参数,您可以传递 Mix.Shell.IOMix.Shell.ProcessMix.Shell.Quiet 或任何实现了 Mix.Shell 行为的模块。

调用此函数后,shell 成为 shell/0 返回的 shell。

示例

iex> Mix.shell(Mix.Shell.IO)
:ok

您可以使用 shell/0shell/1 来临时切换 shell,例如,如果您要运行通常会产生大量输出的 Mix 任务

shell = Mix.shell()
Mix.shell(Mix.Shell.Quiet)

try do
  Mix.Task.run("noisy.task")
after
  Mix.shell(shell)
end
@spec target() :: atom()

返回 Mix 目标。

@spec target(atom()) :: :ok

将当前 Mix 目标更改为 target

调用此函数时要小心,因为任何项目配置都不会重新加载。