相依性
宣告相依性
相依性可以在頂層 rebar.config
檔案中宣告,並使用 rebar3 tree
指令檢查。
一般來說,Rebar3 支援兩種相依性
- 原始碼相依性 (Git, Mercurial)
- 套件相依性
兩種相依性的運作方式大致相同。Rebar3 使用 hex.pm 來提供一組受管理的套件及其相依性。它們通常會更快(在 CDN 後面),可以鏡像,並且會在本地 ~/.cache/rebar3/
中快取。
所有相依性都是專案本地的。這通常是一個不錯的選擇,可以避免全域庫版本衝突的常見問題。它還有助於 Erlang 的 發行版本 機制,該機制構建獨立系統。
相依性符合以下任何格式
{deps,[
%% Packages
rebar,
{rebar,"1.0.0"},
{rebar, {pkg, rebar_fork}}, % rebar app under a different pkg name
{rebar, "1.0.0", {pkg, rebar_fork}},
%% Source Dependencies
{rebar, {git, "git://github.com/erlang/rebar3.git"}},
{rebar, {git, "http://github.com/erlang/rebar3.git"}},
{rebar, {git, "https://github.com/erlang/rebar3.git"}},
{rebar, {git, "git@github.com:erlang/rebar3.git"}},
{rebar, {hg, "https://othersite.com/erlang/rebar3"}},
{rebar, {git, "git://github.com/erlang/rebar3.git", {ref, "aef728"}}},
{rebar, {git, "git://github.com/erlang/rebar3.git", {branch, "master"}}},
{rebar, {git, "git://github.com/erlang/rebar3.git", {tag, "3.0.0"}}},
%% Source dependencies (git only) in subdirectories, from version 3.14 onwards
{rebar, {git_subdir, "git://github.com/erlang/rebar3.git", {branch, "main"}, "subdir"}},
{rebar, {git_subdir, "git://github.com/erlang/rebar3.git", {tag, "3.14"}, "sub/dir"},
{rebar, {git_subdir, "git://github.com/erlang/rebar3.git", {ref, "aeaefd"}, "dir"}
]}.
如上例所示,目前版本僅支援套件、git 來源和 mercurial 來源。自定義相依性來源可以通過 實作資源行為 並像插件一樣包含它來添加。
運行時相依性
但是,Erlang/OTP 為了啟動和關閉應用程式而進行的相依性處理,以及用於構建發行版本和腳本的工具(甚至是 Rebar3 的一部分),都依賴於更精細的相依性宣告,指定專案中每個應用程式依賴於其他應用程式。
您應該將每個相依性添加到您的 app
或 app.src
檔案中
{application, <APPNAME>,
[{description, ""},
{vsn, "<APPVSN>"},
{registered, []},
{modules, []},
{applications, [kernel
,stdlib
,cowboy
]},
{mod, {<APPNAME>_app, []}},
{env, []}
]}.
這將允許靈活地編寫和產生軟體,其中各種不相交的應用程式可以在虛擬機器中共存,而它們的相依性不會完全糾纏在一起。例如,您可能希望您的 Web 服務器能夠獨立於管理和除錯工具運行,即使它們應該在生產環境中可用。
如果需要支援更多格式,可以通過 rebar_resource
行為 擴展 Rebar3,並通過 Pull Request 將其發送給維護人員。
🚧
相依性和設定檔
相依性將始終使用應用於其設定的
prod
設定檔進行編譯。任何其他設定檔(當然,除了default
之外)都不會用於任何相依性。即使它們被設定為prod
,相依性仍然會被提取到其宣告的設定檔的目錄中。例如,頂層deps
中的相依性將位於_build/default/lib
下,而test
設定檔下的相依性將被提取到_build/test/lib
,並且兩者都將使用其prod
設定檔設定進行編譯。
相依性版本處理
Rebar3 認為相依性版本僅供參考。鑑於添加 Rebar3 時 Erlang 社群中現有的開源環境,嘗試強加 語義化版本控制 或任何其他類似方案被認為是不切實際的
- 人們更新*一些*版本,但不是全部版本(Git 標籤 vs. 分支名稱 vs. OTP 應用程式版本),它們可能相互矛盾;
- 有些人從不更新他們的版本,並在它們下多次發布;
- 並非每個人都訂閱相同的版本方案;
- 人們在訂閱語義化版本控制時會犯錯誤;
- 許多應用程式卡在小於
1.0.0
的版本中,因此永遠被認為是不穩定的; - 經常使用來源相依性:因此,找出版本衝突需要每次都從所有相依性下載所有遞移相依性,以確定它們是否衝突,這成本很高;
- 嚴格遵守語義化版本控制最終會導致錯誤的衝突,其中使用在主要版本中沒有更改的 API 子集仍然需要手動解決衝突和相依性審計。
相反,Rebar3 將以 層次遍歷 的方式提取和下載相依性。這表示最接近相依性樹根的相依性將被選擇,而不管其版本如何。專案 rebar.config
中宣告的任何相依性永遠不會被遞移相依性覆蓋,並且遞移相依性永遠不會被稍後遇到的衝突遞移相依性覆蓋。
這也表示,如果您希望優先使用某個版本,則只需將其添加到 rebar.config
檔案中並選擇要保留的內容即可;語義化版本控制有時也需要相同的衝突解決機制。
在實踐中,這已被證明是一種絕對足夠的機制。
每次運行相依性提取和解析後,最終相依性列表都會寫入 rebar.lock
。
📘
將衝突視為錯誤
如果您希望 Rebar3 在檢測到相依性衝突時立即中止,而不是跳過檔案並照常進行,請在您的 rebar 設定檔中添加一行
{deps_error_on_conflict, true}.
。
為了方便起見(並且因為 hex.pm 規定了 semver),可以使用類似 semver 的語法指定 hex 相依性
{deps,[
rebar, % fetches latest known version, ignoring pre-releases
{rebar, "~> 2.0.0"}, % >= 2.0.0 and < 2.1.0`
{rebar, "~> 2.1.2"}, % >= 2.1.2 and < 2.2.0`
{rebar, "~> 2.1.3-dev"}` % >= 2.1.3-dev and < 2.2.0`
{rebar, "~> 2.0"}` % >= 2.0.0 and < 3.0.0`
{rebar, "~> 2.1"}` % >= 2.1.0 and < 3.0.0`
]}.
要取得可用的最新版本套件,請呼叫
$ rebar3 update
===> Updating package index...
要使用預設 CDN 以外的 CDN,例如 官方鏡像 之一,請添加到您專案的 rebar.config
或 ~/.config/rebar3/rebar.config
{rebar_packages_cdn, "https://s3-eu-west-1.amazonaws.com/s3-eu.hex.pm"}.
Checkout 相依性
為了處理您希望在本地處理的相依性,而無需不斷發布新版本的情況,可以使用 _checkouts
目錄。只需在專案的頂層建立一個指向您的相依性的符號連結或複製您的相依性到 _checkouts
_checkouts
└── depA
└── src
如果應用程式或插件同時列在 rebar.config
的 deps
、plugins
或 project_plugins
中,則 _checkouts
中的任何應用程式或插件都將優先於相同的應用程式。這也會覆蓋已提取到 _build
的任何內容。
請注意,_checkouts
是一個覆蓋,這表示它要運作,rebar.config
中*必須*存在 dep
或 plugin
條目。
抓取順序
對於常規的相依性樹,例如
A
/ \
B C
將提取相依性 A
、B
和 C
。
但是,對於更複雜的樹,例如
A
/ \
B C1
|
C2
將提取相依性 A
、B
和 C1
。當 Rebar3 遇到 C2
的需求時,它將顯示警告:`跳過 C2 (來自 $SOURCE),因為已提取同名應用程式。`
此訊息應讓使用者知道已跳過哪個相依性。
如果兩個遞移相依性具有相同的名稱並且位於同一級別,該怎麼辦?
A
/ \
B C
| |
D1 D2
在這種情況下,D1
將取代 D2
,因為 B
按字典順序排序在 C
之前。這是一個完全任意的規則,但它至少是一個確保可重複提取的規則。
如果使用者不同意結果,他們可以將 D2
提升到頂層,並確保它會被及早選擇
A D2
/ \
B C
| |
D1 D2
這將產生 A
、B
、C
和 D2
。
Rebar3 將使用套件執行相同的演算法,並且還會檢測循環相依性並在這些相依性上出錯。
_checkouts
目錄中的相依性將保持不變,並被視為頂級 OTP 應用程式。
鎖定檔案
鎖定檔案 (rebar.lock
) 是 Rebar3 產生的少數存在於 _build/
之外的產物之一。它們應該始終被簽入版本控制。鎖定檔案包含有關程式碼相依性的資訊,包括 git 中來源相依性的不可變參考,以及它們的版本以及套件的預期雜湊值(可用於防止鏡像被劫持)。
目標是使用有關找到的相依性的更準確的資訊,而不是僅通過設定檔獲得的資訊,例如,允許將相依性設定為根據需要從 main
更新,但在同時鎖定到已測試的穩定版本。只有解鎖或升級相依性才能將其移至更新或不同的版本。這個想法是允許可重複的構建,即使例如 Git 標籤或分支被某人破壞性地修改。
在切換分支或提取遞移相依性時,Rebar3 還將使用鎖定檔案作為相依性的真正權威來源(如果有的話,它將從鎖定檔案中選擇數據),而不是 rebar.config
檔案。這樣,當在 rebar.config
檔案中使用鬆散的參考或版本時,我們可以將安全的測試狀態帶入其他應用程式。
預計格式將向前和向後相容。Rebar3 將根據儲存的元數據格式註釋鎖定檔案版本,並在使用舊版本的 Rebar3 讀取較新版本的鎖定檔案時發出警告。這可以告訴使用者,使用舊版本的工具會遺失一些在以後被認為重要的元數據。
升級相依性
每當提取和鎖定相依性時,Rebar3 都將從來源檔案中提取一個參考,以將其固定到特定時間點的版本。相依性應強制在後續構建中遵循該版本。
Rebar3 可以通過兩種方式將先前安裝的相依性升級到較新版本:將分支的參考轉發到其最新版本(例如,將舊的 main
分支更新到新的 main
的 HEAD
以獲取來源檔案,或者獲取未指定版本的套件的最新版本),或者使用來自 rebar.config
檔案的新版本覆蓋現有的鎖定相依性。
在以下相依性樹中
A B
| |
C D
使用者可以升級任何一個相依性(rebar3 upgrade A
和 rebar3 upgrade B
)或同時升級兩者(rebar3 upgrade A,B
或 rebar3 upgrade
,這將升級*所有*相依性)。
僅升級 A
意味著 A
和 C
可能會被升級。針對 B
和 D
的升級將會被忽略。
升級依賴項可能會產生意料之外的結果和有趣的邊緣情況。請考慮以下依賴樹
A B C1
/ \ / \ / \
D E F G H I2
| |
J K
|
I1
在取得上述依賴樹之後,由於 I2
比 I1
更接近專案根目錄,因此會優先選擇 I2
。然而,在從 C1
升級到 C2
之後,如果 C2
不再需要依賴 I2
,Rebar3 將會自動在 A
樹狀結構下取得 I1
(即使 A
不需要升級),以提供正確的新樹狀結構。升級後的樹狀結構如下所示
A B C2
/ \ / \ |
D E F G H
| |
J K
|
I1
其中 I2
不再存在於專案中,而 I1
則存在。
呼叫 rebar3 unlock
將會完全清除鎖定檔案。
您可以使用 rebar3 tree
手動檢查,它將會顯示目前的依賴樹
$ rebar3 tree
...
├─ bootstrap-0.0.2 (git repo)
├─ dirmon-0.1.0 (project app)
├─ file_monitor-0.1 (git repo)
├─ peeranha-0.1.0 (git repo)
│ ├─ gproc-git (git repo)
│ ├─ interclock-0.1.2 (git repo)
│ │ ├─ bitcask-1.7.0 (git repo)
│ │ │ └─ lager-2.1.1 (hex package)
│ │ │ └─ goldrush-0.1.6 (hex package)
│ │ └─ itc-1.0.0 (git repo)
│ └─ merklet-1.0.0 (git repo)
├─ recon-2.2.2 (git repo)
└─ uuid-1.5.0 (git repo)
└─ quickrand-1.5.0 (git repo)
Elixir 相依性
Rebar3 3.7.0 版開始支援 Elixir 依賴項,Elixir 1.7.4 版則透過插件支援。詳情請參閱相關的插件章節。