自訂依賴資源

與 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_staterebar_app_inforebar_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 設定值中的類型(githg 等),而 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.configrebar.lockdeps 資訊。無法獲得任何與專案設定相關的資訊,這基本上限制了每個資源可以執行的操作。

這些自訂資源在高於 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_resourcerebar_hg_resourcerebar_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 提供可接受的(即使是降級的)使用者體驗時,這種方法最有用。

上次修改日期:2022 年 12 月 9 日:修復損壞的原始程式碼連結 (33b768f)