自訂依賴資源
與 3.7.0 及以上版本相容的資源
從 3.7.0 版本開始,Rebar3 發布了一個新的自訂資源 API,它可以訪問專案的本地設定,以啟用更強大的自訂依賴格式。它們可以使用來自當前建置的上下文資訊來自訂如何擷取依賴項。
🚧
新的介面不向後相容
這個新的介面在 3.7.0 之前的版本中是未知且不受支援的。如果您正在編寫應該適用於所有 Rebar3 副本的程式庫,請跳到下一節,其中記錄了與所有 Rebar3 版本相容的資源。然而,舊介面仍然與所有版本相容,並且在添加新 API 時,沒有破壞對現有專案的支援。
新的回呼 API 定義如下
%% Type declarations
-type resource() :: #resource{}. % an opaque record generated by an API call described below
-type source() :: {type(), location(), ref()} | {type(), location(), ref(), binary()}.
-type type() :: atom().
-type location() :: string().
-type ref() :: any().
-type resource_state() :: term().
%% and the following callbacks
-callback init(type(), rebar_state:t()) -> {ok, resource()}.
-callback lock(rebar_app_info:t(), resource_state()) -> source().
-callback download(file:filename_all(), rebar_app_info:t(), rebar_state:t(), resource_state()) ->
ok | {error, any()}.
-callback needs_update(rebar_app_info:t(), resource_state()) -> boolean().
-callback make_vsn(rebar_app_info:t(), resource_state()) ->
{plain, string()} | {error, string()}.
回呼允許資源插件訪問 rebar_state:t()
資料結構,讓您可以訪問和操作 Rebar3 狀態、查找應用程式狀態,以及通常使用 rebar_state
、rebar_app_info
、rebar_dir
和新的 rebar_paths
模組。
使用此功能的插件範例是 rebar3_path_deps。Rebar3 自己的 hex 套件資源 使用此 API。
資源插件的初始化方式與任何其他插件相同
-module(my_rebar_plugin).
-export([init/1]).
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
{ok, rebar_state:add_resource(State, {Tag, Module})}.
其中 Tag
代表 deps
設定值中的類型(git
、hg
等),而 Module
是回呼模組。
回呼模組可能如下所示
-module(my_rebar_plugin_resource).
-export([init/2,
lock/2,
download/4,
needs_update/2,
make_vsn/1]).
%% Initialize the custom dep resource plugin
init(Type, _RebarState) ->
CustomState = #{},
Resource = rebar_resource_v2:new(
Type, % type tag such as 'git' or 'hg'
?MODULE, % this callback module
CustomState % anything you want to carry around for next calls
),
{ok, Resource}.
lock(AppInfo, CustomState) ->
%% Extract info such as {Type, ResourcePath, ...} as declared
%% in rebar.config
SourceTuple = rebar_app_info:source(AppInfo),
%% Annotate and modify the source tuple to make it absolutely
%% and indeniably unambiguous (for example, with git this means
%% transforming a branch name into an immutable ref)
...
%% Return the unambiguous source tuple
ModifiedSource.
download(TmpDir, AppInfo, RebarState, CustomState) ->
%% Extract info such as {Type, ResourcePath, ...} as declared
%% in rebar.config
SourceTuple = rebar_app_info:source(AppInfo)),
%% Download the resource defined by SourceTuple, which should be
%% an OTP application or library, into TmpDir
...
ok.
make_vsn(Dir, CustomState) ->
%% Extract a version number from the application. This is useful
%% when defining the version in the .app.src file as `{version, Type}',
%% which means it should be derived from the build information. For
%% the `git' resource, this means looking for the last tag and adding
%% commit-specific information
...
{plain, "0.1.2"}.
needs_update(AppInfo, CustomState) ->
%% Extract the Source tuple if needed
SourceTuple = rebar_app_info:source(AppInfo),
%% Base version in the current file
OriginalVsn = rebar_app_info:original_vsn(AppInfo)
%% Check if the copy in the current install matches
%% the defined value in the source tuple. On a conflict,
%% return `true', otherwise `false'
...,
Bool.
與所有版本相容的資源
在 3.7.0 版本之前,依賴資源框架的限制更多。它基本上必須在沒有上下文的情況下工作,只能使用來自 rebar.config
和 rebar.lock
的 deps
資訊。無法獲得任何與專案設定相關的資訊,這基本上限制了每個資源可以執行的操作。
這些自訂資源在高於 3.7.0 的 Rebar3 版本中仍然受支援,因此,如果您有使用者運行較舊的建置,我們建議您僅開發此類資源。
每個依賴資源都必須實作 rebar_resource
行為模式。
-module(rebar_resource).
-export_type([resource/0
,type/0
,location/0
,ref/0]).
-type resource() :: {type(), location(), ref()}.
-type type() :: atom().
-type location() :: string().
-type ref() :: any().
-callback lock(file:filename_all(), tuple()) ->
rebar_resource:resource().
-callback download(file:filename_all(), tuple(), rebar_state:t()) ->
{tarball, file:filename_all()} | {ok, any()} | {error, any()}.
-callback needs_update(file:filename_all(), tuple()) ->
boolean().
-callback make_vsn(file:filename_all()) ->
{plain, string()} | {error, string()}.
rebar3
中包含 rebar_git_resource、rebar_hg_resource 和 rebar_pkg_resource。
自訂資源可以像插件一樣被包含。Kelly McLaughlin 的 rebar3_tidy_deps 資源 中可以看到一個例子
-module(rebar_tidy_deps).
-export([init/1]).
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
{ok, rebar_state:add_resource(State, {github, rebar_github_resource})}.
這個實作 rebar3
資源行為模式的資源 rebar_github_resource
會被添加到 rebar_state
中可用的資源列表中。將儲存庫作為插件添加到 rebar.config
允許使用此資源
{mydep, {github, "kellymclauglin/mydep.git", {tag, "1.0.1"}}}.
{plugins, [
{rebar_tidy_deps, ".*", {git, "https://github.com/kellymclaughlin/rebar3-tidy-deps-plugin.git", {tag, "0.0.2"}}}
]}.
編寫適用於兩個版本的插件
如果您想編寫適用於兩個版本的自訂資源插件,您可以動態偵測參數以提供向後相容的功能。在下面的範例中,新 API 會忽略所有新資訊,並將自身重新插入舊 API 中
-module(my_rebar_plugin_resource).
-export([init/2,
lock/2,
download/4, download/3,
needs_update/2,
make_vsn/1]).
init(Type, _RebarState) ->
CustomState = #{},
Resource = rebar_resource_v2:new(Type, ?MODULE, CustomState),
{ok, Resource}.
%% Old API
lock(Dir, Source) when is_tuple(Source) ->
lock_(Dir, Source);
%% New API
lock(AppInfo, _ResourceState) ->
%% extract info for dir extract info for source
lock_(rebar_app_info:dir(AppInfo), rebar_app_info:source(AppInfo)).
%% Function handling normalized case
lock_(Dir, Path) ->
...
%% Old Version
download(TmpDir, SourceTuple, RebarState) ->
download_(TmpDir, SourceTuple, State).
%% New Version
download(TmpDir, AppInfo, RebarState, _ResourceState) ->
%% extract source tuple
download_(TmpDir, rebar_app_info:source(AppInfo), RebarState).
%% Function handling normalized case
download_(TmpDir, {MyTag, ...}, _State) ->
...
%% Old version
make_vsn(Dir) ->
...
%% New version
make_vsn(Dir, _ResourceState) ->
make_vsn(Dir).
%% Old Version
needs_update(Dir, {MyTag, Path, _}) ->
needs_update_(Dir, {MyTag, Path});
%% New Version
needs_update(AppInfo, _) ->
needs_update_(rebar_app_info:dir(AppInfo), rebar_app_info:source(AppInfo)).
%% Function handling normalized case
needs_update_(Dir, {Tag, Path}) ->
...
請注意,如果您的資源確實需要新的 API 才能工作,則向後相容性將難以實現,因為無論何時調用它,它都不會擁有新 API 的所有資訊。
當您可以使用舊 API 提供可接受的(即使是降級的)使用者體驗時,這種方法最有用。