檢視原始碼 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 任務可以直接從命令列執行
mix compile
- 編譯目前的專案mix test
- 執行指定專案的測試mix run
- 在專案內執行特定命令
每個任務都有自己的選項,有時在 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.exs
、config/test.exs
和 config/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 cmd
、mix do
、mix 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
不打算儲存值的環境變數(基本上作為標記),應設定為 1
或 true
,例如
$ 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
。
確保 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
)。
安裝並啟動相依性。
指定的 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
,則會忽略此環境變數。
@spec install_project_dir() :: Path.t() | nil
傳回目前 Mix.install/2
專案所在的目錄。
傳回是否在目前的節點中呼叫 Mix.install/2
。
@spec path_for(:archives | :escripts) :: String.t()
本機封存檔或 escripts 的路徑。
引發格式良好的 Mix 錯誤,預設退出狀態為 1
。
@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.IO
、Mix.Shell.Process
、Mix.Shell.Quiet
或任何實作 Mix.Shell
行為的模組作為引數。
呼叫此函式後,shell
會變成 shell/0
傳回的 shell。
範例
iex> Mix.shell(Mix.Shell.IO)
:ok
您可以使用 shell/0
和 shell/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
。
呼叫此函數時請小心,因為任何專案組態都不會重新載入。