檢視原始碼 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.Project 模組以取得 Mix 專案的詳細文件。

定義專案後,許多預設 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.Task 行為以取得 Mix 任務的詳細文件。

相依性

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 - Mix 安裝的 rebar3 指令的覆寫路徑(預設值:~/.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

本機封存檔或 escripts 的路徑。

引發格式良好的 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()

本機封存檔或 escripts 的路徑。

@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

呼叫此函數時請小心,因為任何專案組態都不會重新載入。