-module(rebar_compiler_mib). -behaviour(rebar_compiler). -export([context/1, needed_files/4, dependencies/3, compile/4, clean/2]). -include("rebar.hrl"). -include_lib("stdlib/include/erl_compile.hrl"). context(AppInfo) -> Dir = rebar_app_info:dir(AppInfo), Mappings = [{".bin", filename:join([Dir, "priv", "mibs"])}, {".hrl", filename:join(Dir, "include")}], #{src_dirs => ["mibs"], include_dirs => [], src_ext => ".mib", out_mappings => Mappings}. needed_files(_, FoundFiles, _, AppInfo) -> RebarOpts = rebar_app_info:opts(AppInfo), MibFirstConf = rebar_opts:get(RebarOpts, mib_first_files, []), valid_mib_first_conf(MibFirstConf), Dir = rebar_app_info:dir(AppInfo), MibFirstFiles = [filename:join(Dir, File) || File <- MibFirstConf], %% Remove first files from found files RestFiles = [Source || Source <- FoundFiles, not lists:member(Source, MibFirstFiles)], Opts = rebar_opts:get(rebar_app_info:opts(AppInfo), mib_opts, []), {{MibFirstFiles, Opts}, {RestFiles, Opts}}. valid_mib_first_conf(FileList) -> Strs = filter_file_list(FileList), case rebar_utils:is_list_of_strings(Strs) of true -> true; false -> ?ABORT("An invalid file list (~p) was provided as part of your mib_first_files directive", [FileList]) end. filter_file_list(FileList) -> Atoms = lists:filter( fun(X) -> is_atom(X) end, FileList), case Atoms of [] -> FileList; _ -> atoms_in_mib_first_files_warning(Atoms), lists:filter( fun(X) -> not(is_atom(X)) end, FileList) end. atoms_in_mib_first_files_warning(Atoms) -> W = "You have provided atoms as file entries in mib_first_files; " "mib_first_files only expects lists of filenames as strings. " "The following MIBs (~p) may not work as expected and it is advised " "that you change these entries to string format " "(e.g., \"mibs/SOME-MIB.mib\") ", ?WARN(W, [Atoms]). dependencies(File, _Dir, SrcDirs) -> SrcFiles = lists:append([src_files(SrcDir) || SrcDir <- SrcDirs]), file_deps(File, SrcFiles). compile(Source, OutDirs, _, Opts) -> {_, BinOut} = lists:keyfind(".bin", 1, OutDirs), {_, HrlOut} = lists:keyfind(".hrl", 1, OutDirs), ok = rebar_file_utils:ensure_dir(BinOut), ok = rebar_file_utils:ensure_dir(HrlOut), Mib = filename:join(BinOut, filename:basename(Source, ".mib")), HrlFilename = Mib ++ ".hrl", AllOpts = [{outdir, BinOut}, {i, [BinOut]}] ++ Opts, case snmpc:compile(Source, AllOpts) of {ok, _} -> MibToHrlOpts = case proplists:get_value(verbosity, AllOpts, undefined) of undefined -> #options{specific = [], cwd = rebar_dir:get_cwd()}; Verbosity -> #options{specific = [{verbosity, Verbosity}], cwd = rebar_dir:get_cwd()} end, ok = snmpc:mib_to_hrl(Mib, Mib, MibToHrlOpts), rebar_file_utils:mv(HrlFilename, HrlOut), ok; {error, compilation_failed} -> ?ABORT end. clean(MibFiles, AppInfo) -> AppDir = rebar_app_info:dir(AppInfo), MIBs = [filename:rootname(filename:basename(MIB)) || MIB <- MibFiles], rebar_file_utils:delete_each( [filename:join([AppDir, "include", MIB++".hrl"]) || MIB <- MIBs]), ok = rebar_file_utils:rm_rf(filename:join([AppDir, "priv/mibs/*.bin"])). src_files(Dir) -> %% .mib extension is assumed to be valid here case file:list_dir(Dir) of {ok, Files} -> [filename:join(Dir, File) || File <- Files, filename:extension(File) =:= ".mib"]; _ -> [] end. file_deps(File, Files) -> DepMods = imports_in_file(File), lists:filter( fun(F) -> Mods = modules_in_file(F), lists:any(fun(M) -> lists:member(M, Mods) end, DepMods) end, Files ). modules_in_file(File) -> {ok, Bin} = file:read_file(File), Res = re:run( Bin, "^([a-zA-Z0-9_-]+)\\s+DEFINITIONS\\s+::=\\s+BEGIN", [multiline, {capture, all_but_first, list}, global, unicode] ), case Res of nomatch -> []; {match, List} -> lists:usort(lists:append(List)) end. imports_in_file(File) -> {ok, Bin} = file:read_file(File), ImportMatch = re:run( Bin, "IMPORTS\\s+(.*);", [multiline, dotall, ungreedy, {capture, all_but_first, list}, global] ), case ImportMatch of nomatch -> []; {match, ImportSections} -> Modules = re:run( ImportSections, "FROM\\s+([a-zA-Z0-9_-]+)\\s+", [multiline, {capture, all_but_first, list}, global] ), case Modules of nomatch -> []; {match, List} -> lists:usort(lists:append(List)) end end.