Current section
Files
Jump to
Current section
Files
src/clean_diameter.erl
%% -------------------------------------------------------------------
%%
%% @doc Rebar3 plugin for cleaning compiled diameter files.
%%
%% This module provides cleanup functionality for files generated by the
%% diameter compiler plugin. It removes generated .erl and .hrl files,
%% but preserves the original .dia source files.
%%
%% The cleaner automatically discovers which files were generated based on
%% the .dia files present in the project and removes only those files that
%% would be regenerated by compilation.
%%
%% == Usage ==
%%
%% The plugin is typically invoked automatically via provider hooks:
%% ```
%% rebar3 diameter clean
%% '''
%%
%% @author Carlos Eduardo de Paula
%% @since 1.0.0
%% @end
%%
%% -------------------------------------------------------------------
-module(clean_diameter).
-export([init/1, do/1, format_error/1]).
-define(PROVIDER, clean).
-define(DEPS, [{default, app_discovery}]).
%%%===================================================================
%%% API
%%%===================================================================
%% @doc Initialize the diameter clean provider.
%%
%% Sets up the rebar3 provider for cleaning diameter-generated files.
%% This provider removes .erl and .hrl files that were generated from
%% .dia sources during compilation.
%%
%% @param State The current rebar3 state
%% @returns {ok, UpdatedState} with the provider registered
%% @end
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Provider =
%% The 'user friendly' name of the task
providers:create([
{name, ?PROVIDER},
%% The module implementation of the task
{module, ?MODULE},
{bare,
%% The task can be run by the user, always true
true},
%% The list of dependencies
{deps, ?DEPS},
%% How to use the plugin
{example, "rebar diameter clean"},
%% list of options understood by the plugin
{opts, []},
{short_desc, "Clean compiled diameter files."},
{desc, ""},
{namespace, diameter}
]),
{ok, rebar_state:add_provider(State, Provider)}.
%% @doc Execute the diameter cleanup process.
%%
%% This function performs the cleanup work by:
%% <ol>
%% <li>Discovering all .dia files in the project</li>
%% <li>Determining which .erl and .hrl files they generated</li>
%% <li>Removing those generated files</li>
%% </ol>
%%
%% @param State The current rebar3 state
%% @returns {ok, State} after cleanup is complete
%% @end
-spec do(rebar_state:t()) -> {ok, rebar_state:t()}.
do(State) ->
rebar_api:info("Cleaning compiled diameter files...", []),
Apps =
case rebar_state:current_app(State) of
undefined ->
rebar_state:project_apps(State);
AppInfo ->
[AppInfo]
end,
lists:foreach(fun(App) -> clean(State, App) end, Apps),
{ok, State}.
%% @doc Format error messages for user display.
%%
%% @param Reason The error term to format
%% @returns Formatted error message as iolist
%% @end
-spec format_error(any()) -> iolist().
format_error(Reason) ->
io_lib:format("~p", [Reason]).
%% @doc Clean generated files for a specific application.
%%
%% Discovers .dia files and removes their corresponding generated .erl and .hrl files.
%% Only removes files that can be regenerated from .dia sources.
%%
%% @param State Rebar3 state containing configuration
%% @param _AppFile Application info (directory paths extracted from this)
%% @returns ok after cleanup
%% @private
clean(State, _AppFile) ->
AppDir = rebar_app_info:dir(_AppFile),
EbinDir = rebar_app_info:ebin_dir(_AppFile),
DiaOpts = rebar_state:get(State, dia_opts, []),
IncludeEbin = proplists:get_value(include, DiaOpts, []),
code:add_pathsz([EbinDir | IncludeEbin]),
GeneratedFiles = dia_generated_files(AppDir, "dia", "src", "include", DiaOpts),
ok = rebar_file_utils:delete_each(GeneratedFiles),
ok.
%% ===================================================================
%% Internal functions
%% ===================================================================
%% @doc Find all files generated from diameter dictionaries.
%%
%% Scans for .dia files and determines what .erl and .hrl files they would
%% generate based on parsing their specifications. This ensures we only
%% remove files that are actually generated content.
%%
%% @param AppDir Application root directory
%% @param DiaDir Directory containing .dia files (relative to AppDir)
%% @param SrcDir Directory containing generated .erl files (relative to AppDir)
%% @param IncDir Directory containing generated .hrl files (relative to AppDir)
%% @param DiaOpts Diameter compilation options
%% @returns List of file paths to remove
%% @private
dia_generated_files(AppDir, DiaDir, SrcDir, IncDir, DiaOpts) ->
F = fun(File, Acc) ->
try diameter_dict_util:parse({path, File}, DiaOpts) of
{ok, Spec} ->
FileName = dia_filename(File, Spec),
[
filename:join([AppDir, IncDir, FileName ++ ".hrl"])
| filelib:wildcard(
filename:join([
AppDir,
SrcDir,
FileName ++
".*"
])
)
] ++
Acc;
_ ->
Acc
catch
_:_ ->
Acc
end
end,
lists:foldl(
F,
[],
filelib:wildcard(
filename:join([AppDir, DiaDir, "*.dia"])
)
).
%% @doc Extract the dictionary name from file or spec.
%%
%% Same logic as in compile_diameter.erl - determines the name to use for
%% generated files based on the diameter specification or source filename.
%%
%% @param File Path to the source .dia file
%% @param Spec Parsed diameter dictionary specification
%% @returns String name for the dictionary
%% @private
dia_filename(File, Spec) ->
case proplists:get_value(name, Spec) of
undefined ->
filename:rootname(
filename:basename(File)
);
Name ->
Name
end.