| %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- |
| %% ex: ts=4 sw=4 et |
| %% ------------------------------------------------------------------- |
| %% |
| %% rebar: Erlang Build Tools |
| %% |
| %% Copyright (c) 2014 Tomas Abrahamsson (tomas.abrahamsson@gmail.com) |
| %% |
| %% Permission is hereby granted, free of charge, to any person obtaining a copy |
| %% of this software and associated documentation files (the "Software"), to deal |
| %% in the Software without restriction, including without limitation the rights |
| %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| %% copies of the Software, and to permit persons to whom the Software is |
| %% furnished to do so, subject to the following conditions: |
| %% |
| %% The above copyright notice and this permission notice shall be included in |
| %% all copies or substantial portions of the Software. |
| %% |
| %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| %% THE SOFTWARE. |
| %% ------------------------------------------------------------------- |
| -module(rebar_proto_compiler). |
| |
| -export([compile/2, |
| clean/2]). |
| |
| %% for internal use only |
| -export([info/2]). |
| |
| -include("rebar.hrl"). |
| |
| -record(proto_compiler, |
| {key :: atom(), % Corresponds to the {proto_compiler,Key} |
| module :: atom() |
| }). |
| |
| %% =================================================================== |
| %% Public API |
| %% =================================================================== |
| |
| find_proto_files(ProtoDirs) -> |
| lists:foldl(fun(ProtoDir, Acc) -> |
| rebar_utils:find_files_by_ext(ProtoDir, ".proto") ++ Acc |
| end, |
| [], ProtoDirs). |
| |
| compile(Config, AppFile) -> |
| %% Find a compiler for protocol buffers, |
| %% use that for compiling protocol buffers |
| {CompilerModule, ProtoDirs} = select_proto_compiler_and_dir(Config), |
| case find_proto_files(ProtoDirs) of |
| [] -> |
| ok; |
| Protos -> |
| %% Ask the proto compiler to compile the .proto files. |
| CompilerModule:proto_compile(Config, AppFile, Protos) |
| end. |
| |
| clean(Config, AppFile) -> |
| %% Find a compiler for protocol buffers, |
| %% use that for clean protocol buffers |
| {CompilerModule, ProtoDirs} = select_proto_compiler_and_dir(Config), |
| %% Get a list of generated .beam and .hrl files and then delete them |
| case find_proto_files(ProtoDirs) of |
| [] -> |
| ok; |
| Protos -> |
| %% Ask the proto compiler to clean the .proto files. |
| CompilerModule:proto_clean(Config, AppFile, Protos) |
| end. |
| |
| %% =================================================================== |
| %% Internal functions |
| %% =================================================================== |
| |
| info(help, compile) -> |
| info_help("Build protocol buffer (*.proto) sources", compile); |
| info(help, clean) -> |
| info_help("Delete protocol buffer (*.proto) build results", clean). |
| |
| info_help(GeneralDescr, Cmd) -> |
| ?CONSOLE( |
| "~s.~n" |
| ++ "~n" |
| ++ "Valid rebar.config options:~n" |
| ++ " {proto_opts, [~n" |
| ++ " {compiler, Compiler},~n" |
| ++ " {src_dirs, [Dir]}~n" |
| ++ " ]}~n" |
| ++ "The following protocol buffer compilers are available:~n" |
| ++ "~s~n", |
| [GeneralDescr, format_proto_compiler_list()]), |
| %% Print info for each proto compiler |
| _ = [begin |
| ?CONSOLE("--- ~p ---~n", [Key]), |
| CompilerModule:proto_info(help, Cmd) |
| end |
| || #proto_compiler{key=Key, |
| module=CompilerModule} <- find_proto_compilers()], |
| ok. |
| |
| get_default_compiler() -> |
| protobuffs. |
| |
| find_proto_compilers() -> |
| {ok, RebarModuleGroups} = application:get_env(rebar, modules), |
| {app_dir, Modules} = lists:keyfind(app_dir, 1, RebarModuleGroups), |
| [#proto_compiler{key = M:key(), |
| module = M} |
| || M <- Modules, |
| is_proto_compiler_module(M)]. |
| |
| is_proto_compiler_module(Module) -> |
| case code:ensure_loaded(Module) of |
| {module, Module} -> |
| lists:all(fun({Function, Arity}) -> |
| erlang:function_exported(Module, Function, Arity) |
| end, |
| [{key, 0}, |
| {proto_compile, 3}, |
| {proto_clean, 3}, |
| {proto_info, 2}]); |
| _ -> |
| false |
| end. |
| |
| select_proto_compiler_and_dir(Config) -> |
| Default = get_default_compiler(), |
| ProtoOpts = rebar_config:get_local(Config, proto_opts, []), |
| Key = proplists:get_value(compiler, ProtoOpts, Default), |
| ProtoDirs = proplists:get_value(src_dirs, ProtoOpts, ["src"]), |
| AvailCompilers = find_proto_compilers(), |
| CompilerModule = case lists:keyfind(Key, #proto_compiler.key, AvailCompilers) of |
| #proto_compiler{module=Module} -> |
| Module; |
| false -> |
| ?ABORT("No such protocol buffer compiler known, '~s'~n" |
| ++ "The following are known:~n" |
| ++ "~s~n", |
| [Key, format_proto_compiler_list()]) |
| end, |
| {CompilerModule, ProtoDirs}. |
| |
| format_proto_compiler_list() -> |
| Default = get_default_compiler(), |
| Keys = [Key || #proto_compiler{key=Key} <- find_proto_compilers()], |
| Annotations = [if Key == Default -> " (default)"; |
| true -> "" |
| end |
| || Key <- Keys], |
| lists:flatten( |
| [?FMT(" ~p~s~n", [Key, Annotation]) |
| || {Key, Annotation} <- lists:zip(Keys, Annotations)]). |