插件任务:
在用户执行rebar3 compile时,自动运行插件自定义的逻辑。
增加钩子:
在顶层目录的rebar3.config中增加配置(provider hooks的写法)
{provider_hooks, [
{pre, [{compile, {auto_config, compile}}]}
]}.
意为 rebar3 compile命令前,自动执行 rebar3 auto_config compile 指令
或者 shell hooks 的写法:
{pre_hooks, [“win32”, compile, “rebar3 auto_config compile”]}
新建插件文件:
在新建插件的目录下执行:
rebar3 new plugin 插件名字
输出为:
===> Writing rebar3_auto_config_plugin/src/rebar3_auto_config_plugin.erl
===> Writing rebar3_auto_config_plugin/src/rebar3_auto_config_plugin_prv.erl
===> Writing rebar3_auto_config_plugin/src/rebar3_auto_config_plugin.app.src
===> Writing rebar3_auto_config_plugin/rebar.config
===> Writing rebar3_auto_config_plugin/.gitignore
===> Writing rebar3_auto_config_plugin/LICENSE
===> Writing rebar3_auto_config_plugin/README.md
_prv文件简介:
文件中有三个回调需要我们实现:
- init(State) - {ok, NewState},进行插件设置。
- do(State) - {ok, NewState} | {error, Error},执行实际的工作。
- format_error(Error) - 错误发生时打印错误。
插件设置的内容有:
- name:任务的名称。 module:任务的实现模块。
- bare:用户是否可以运行任务。应该设置为true。
- deps:依赖项列表,不需要包含依赖项的依赖项。[dep | {namespace, dep}]
- desc:任务描述,rebar3帮助使用
- short_desc:任务的一行简短描述
- example:任务用法示例,例如“rebar3 my-provider args”
- opts:执行任务的选项列表。形式为 {Key, $Character, ‘StringName’, Spec, HelpText},其中:
- Key 是一个原子,用于通过
{Args, _} = rebar_state:command_parsed_args(State)
Arg = proplists:get_value(Key, Args)
获取选项值; - $Character 是选项的缩写形式。如果命令要作为 -c Arg 输入,则Character 应该为 c
- Spec 可以是类型(atom、binary、boolean、float、integer或string)、默认的类型({Type、Val})或 undefined。
- Key 是一个原子,用于通过
- profiles:配置文件。默认为 [default]。
- namespace:注册命名空间。默认为 default。
添加命名空间:
在 rebar3_auto_config_plugin_prv 文件 init函数中,增加namespace的定义,修改后代码如下:
Provider = providers:create([
{name, ?PROVIDER},
{namespace, auto_config},
......
命名空间的作用是避免?PROVIDER和别的插件或默认命令冲突。
插件的 ?PROVIDER 定义为 compile, 那么当用户执行 rebar3 compile xxx 的时候, rebar3是执行默认的compile指令呢还是插件的指令? 为了区分这种尴尬的情况,namespace字段就排上用场了。现在我们执行 rebar3 auto_config compile XXX, rebar3就知道需要运行的是aoto_config插件指令。
DEPS的宏需要改为
-define(DEPS, [{default, app_discovery}]).
因为我们当前模块定义了新的命名空间,要使用app_discovery的话,需要指明其来源的命名空间。
如果我们没有定义新的命名空间,那么使用默认的宏定义即可。
获取执行插件的app:
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
Apps = case rebar_state:current_app(State) of
undefined -> rebar_state:project_apps(State);
AppInfo -> [AppInfo]
end,
[do_some_thing(AppInfo) || AppInfo <- Apps],
{ok, State}.
在 do_some_thing 中实现插件的功能即可。