設定檔
在任何專案中,總是會根據執行的任務或執行任務的人員角色而需要一組選項。
例如,在 Erlang 專案中,最常見的例子是僅在測試執行時需要的相依性,例如模擬函式庫或特定的測試工具或框架。
Rebar3 使用「_設定檔_」的概念來滿足這些需求。設定檔是一組僅在其中一種特定情境中使用的設定,覆蓋或補充常規設定。它們的目標是能夠支援多種開發使用案例,同時保持事物的可重複性,並且無需外部工具或環境值來完成該工作。
可以透過三種不同的方式指定執行時的設定檔
- 以 `rebar3 as <profile> <command>` 或 `rebar3 as <profile1>,<profile2> <command>` 的方式呼叫 rebar
- 由指定的 Rebar3 指令。例如,`eunit` 和 `ct` 指令_總是_會在執行中新增 `test` 設定檔。
- `REBAR_PROFILE` 環境變數
任何這些形式(甚至全部同時使用)都會讓 Rebar3 知道它應該以其中一個特殊設定檔執行,並據此修改其設定。
設定檔設定可以在主要的 `rebar.config` 檔案中指定,如下所示
{profiles, [{ProfileName1, [Options, ...]},
{ProfileName2, [Options, ...]}]}.
例如,僅針對測試執行新增 `meck` 相依性的測試設定檔可以定義為
{profiles, [{test, [{deps, [meck]}]}]}.
任何設定值都可以放入設定檔中,包括插件、編譯器選項、發行選項等等。
範例
更完整的範例可能如下所示
{deps, [...]}.
{relx, [
...
]}.
{profiles, [
{prod, [
{erl_opts, [no_debug_info, warnings_as_errors]},
{relx, [{dev_mode, false}]}
]},
{native, [
{erl_opts, [{native, {hipe, o3}}]}
]},
{test, [
{deps, [meck]},
{erl_opts, [debug_info]}
]}
]}.
因此,這樣的專案有_四個_不同的設定檔
-
`default`,所有執行的實際設定檔,對應於整體 `rebar.config` 檔案
-
`prod`,在本例中可能用於產生沒有符號連結且具有更嚴格編譯器選項的完整版本
-
`native`,強制使用 HiPE 進行編譯,以加快數學程式碼的執行速度
-
`test`,在測試執行期間載入模擬函式庫並啟用將除錯資訊保留在檔案中。
這些可以透過多種方式組合。以下是一些執行範例
-
`rebar3 ct`:將執行專案的通用測試套件。依序套用的設定檔將是 `default`,然後是 `test`,因為 `ct` 強制使用 `test` 設定檔。
-
`rebar3 as test ct`:將與前者相同。設定檔只套用一次。.
-
`rebar3 as native ct`:將以原生模式執行測試。設定檔的順序將是 `default`,然後是 `native`,最後是 `test`(由執行的指令最後指定)。
-
`rebar3 as test,native ct`:將與上述類似,但略有不同。套用設定檔時,Rebar3 會先展開所有設定檔,然後以正確的順序套用它們。因此,此處的順序將是 `default`,然後是 `test`,然後是 `native`。最後的 `test` 設定檔(由於 `ct` 指令)將被省略,因為它已被套用。這與呼叫 `rebar3 as native ct` 並不完全相同,因為如果 `test` 和 `native` 設定檔都設定了衝突的選項,則設定檔順序將變得重要。
-
`rebar3 release` 將僅以 `default` 設定檔建置版本。
-
`rebar3 as prod release` 將建置沒有開發模式的版本,並使用更嚴格的編譯器選項集。
-
`rebar3 as prod, native release` 將使用上一個指令建置版本,但也將模組編譯為原生模式。.
-
在環境中使用 `REBAR_PROFILE=native` 的 `rebar3 as prod release` 將以上一個指令建置版本,但 `native` 將在 `prod` _之前_ 套用。
因此,設定檔的套用順序為
default
- `REBAR_PROFILE` 值(如果有的話)
- 在命令列的 `as` 部分中指定的設定檔
- 每個個別指令指定的設定檔
因此,設定檔是一種以情境方式指定設定子集的可組合方式。
📘
鎖定相依性
只有在 `rebar.config` 頂層(`default` 設定檔)中列出的相依性才會儲存到 `rebar.lock`。其他相依性將不會被鎖定。
如果有人想要「鎖定生產環境」(意即使用與生產環境相關的設定檔),答案是保留預設設定檔並使用版本,這會產生可在任何時候重複使用的已編譯成品。
選項合併演算法
嘗試自動合併所有設定選項通常很棘手。不同的工具或指令會以不同的方式預期它們,可以是元組列表、屬性列表或要轉換為某種字典的鍵/值對。
為了支援盡可能通用的形式,Rebar3 將它們處理為屬性列表和元組列表的鬆散組合。這表示以下選項都被視為具有鍵 `native`
native
{native, {hipe, o3}}
{native, still, supported}
即使其中一些可能不受工具支援。例如,Erlang 編譯器支援將巨集定義為 ` {d, 'MACRONAME'} ` 或 ` {d, 'MACRONAME', MacroValue} `,但不支援單獨的 `d`,而它確實支援 `native` 和 ` {native, {hipe, o3}} `。
Rebar3 適當地支援所有這些形式,並以函數式方式將它們合併。讓我們以下列設定檔為例
{profiles, [
{prod, [
{erl_opts, [no_debug_info, warnings_as_errors]},
]},
{native, [
{erl_opts, [{native, {hipe, o3}}, {d, 'NATIVE'}]}
]},
{test, [
{erl_opts, [debug_info]}
]}
]}.
以各種順序套用設定檔將會產生不同的 `erl_opts` 選項列表
- `rebar3 as prod,native,test <command>`:`[debug_info, {d, 'NATIVE'}, {native, {hipe, o3}}, no_debug_info, warnings_as_errors]`
- `rebar3 as test,prod,native <command>`:`[{d, 'NATIVE'}, {native, {hipe, o3}}, no_debug_info, warnings_as_errors, debug_info]`
- `rebar3 as native,test,prod <command>`:`[no_debug_info, warnings_as_errors, debug_info, {d, 'NATIVE'}, {native, {hipe, o3}}]`
- `rebar3 as native,prod,test <command>`:`[debug_info, no_debug_info, warnings_as_errors, {d, 'NATIVE'}, {native, {hipe, o3}}]`
請注意,最後套用的設定檔會產生列表中的第一個元素,並且每個設定檔列表中的元素將根據其鍵進行排序。
這將允許 Rebar3 指令以正確的順序提取元素,同時仍然支援需要許多元素共享相同鍵的多值列表(例如 `[{d, 'ABC'}, {d, 'DEF'}]`,這是兩個獨立的巨集!)。不支援重複元素的指令可以在處理完第一個元素後停止處理它們,而那些從中建置字典(或映射)的指令可以選擇按原樣插入它們,或者可以先安全地反轉列表(如果最後處理的元素成為映射中的最後一個元素)。
所有設定檔合併規則都以這種方式安全地處理。插件作者應瞭解這些規則並據此進行規劃。
請注意,在實務中,Erlang 編譯器無法與 `debug_info` 和 `no_debug_info`(這甚至不是一個真正的選項,是由 Rebar3 新增的)良好地配合使用。Rebar3 做了一些神奇的事情來刪除這些特定值的重複以取悅編譯器,但不會將這種禮遇擴展到所有工具。將您的插件設計為使用 `{OptionName, true|false}` 通常是一個好主意。
🚧
相依性和設定檔
相依性將始終使用套用於其設定的 `prod` 設定檔進行編譯。任何相依性都不會使用其他設定檔(當然,除了 `default` 之外)。即使它們被設定為 `prod`,相依性仍然會被提取到它被宣告的設定檔的設定檔目錄中。例如,頂層 `deps` 中的相依性將位於 `_build/default/lib` 下,而 `test` 設定檔下的相依性將被提取到 `_build/test/lib`。兩者都將使用其套用的 `prod` 設定檔設定進行編譯。