Merge pull request #203 from tomas-abrahamsson/pluggable-proto-compilers-gpb

Pluggable proto compilers gpb
This commit is contained in:
Fred Hebert 2014-10-31 09:32:13 -04:00
commit 17e0b14d1d
25 changed files with 779 additions and 41 deletions

1
THANKS
View file

@ -128,3 +128,4 @@ Alexander Verbitsky
Andras Horvath Andras Horvath
Drew Varner Drew Varner
Roberto Aloi Roberto Aloi
Luis Rascao

View file

@ -28,7 +28,9 @@
rebar_neotoma_compiler, rebar_neotoma_compiler,
rebar_otp_app, rebar_otp_app,
rebar_port_compiler, rebar_port_compiler,
rebar_proto_compiler,
rebar_protobuffs_compiler, rebar_protobuffs_compiler,
rebar_proto_gpb_compiler,
rebar_qc, rebar_qc,
rebar_rel_utils, rebar_rel_utils,
rebar_reltool, rebar_reltool,
@ -67,7 +69,9 @@
{modules, [ {modules, [
{app_dir, [ {app_dir, [
rebar_abnfc_compiler, rebar_abnfc_compiler,
rebar_proto_compiler,
rebar_protobuffs_compiler, rebar_protobuffs_compiler,
rebar_proto_gpb_compiler,
rebar_neotoma_compiler, rebar_neotoma_compiler,
rebar_asn1_compiler, rebar_asn1_compiler,
rebar_dia_compiler, rebar_dia_compiler,

4
inttest/proto_gpb/include/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,110 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% -------------------------------------------------------------------
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2014 Luis Rascão (luis.rascao@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(proto_gpb_rt).
-export([files/0,
run/1]).
-include_lib("eunit/include/eunit.hrl").
-define(MODULES,
[foo,
foo_app,
foo_sup]).
-define(GENERATED_MODULES,
[test_gpb,
test2_gpb,
test3_gpb,
test4_gpb,
test5_gpb]).
files() ->
[
{copy, "../../rebar", "rebar"},
{copy, "rebar.config", "rebar.config"},
{copy, "include", "include"},
{copy, "src", "src"},
{create, "ebin/foo.app", app(foo, ?MODULES ++ ?GENERATED_MODULES)}
].
run(_Dir) ->
?assertMatch({ok, _}, retest_sh:run("./rebar prepare-deps", [])),
?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])),
?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
%% Foo includes test_gpb.hrl,
%% So if it compiled, that also means gpb succeeded in
%% generating the test_gpb.hrl file, and also that it generated
%% the .hrl file was generated before foo was compiled.
ok = check_beams_generated(),
?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])),
ok = check_files_deleted(),
ok.
check_beams_generated() ->
check(fun filelib:is_regular/1,
beam_files()).
check_files_deleted() ->
check(fun file_does_not_exist/1,
beam_files() ++ generated_erl_files() ++ generated_hrl_files()).
beam_files() ->
add_dir("ebin", add_ext(?MODULES, ".beam")).
generated_erl_files() ->
add_dir("src", add_ext(?GENERATED_MODULES, ".erl")).
generated_hrl_files() ->
add_dir("include", add_ext(?GENERATED_MODULES, ".hrl")).
file_does_not_exist(F) ->
not filelib:is_regular(F).
add_ext(Modules, Ext) ->
[lists:concat([Module, Ext]) || Module <- Modules].
add_dir(Dir, Files) ->
[filename:join(Dir, File) || File <- Files].
check(Check, Files) ->
lists:foreach(
fun(F) ->
?assertMatch({true, _}, {Check(F), F})
end,
Files).
%%
%% Generate the contents of a simple .app file
%%
app(Name, Modules) ->
App = {application, Name,
[{description, atom_to_list(Name)},
{vsn, "1"},
{modules, Modules},
{registered, []},
{applications, [kernel, stdlib, gpb]}]},
io_lib:format("~p.\n", [App]).

View file

@ -0,0 +1,18 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
{erl_opts,
[
{i, "deps/gpb/include"},
{platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'}
]}.
{deps,
[
{gpb, [], {git, "git://github.com/tomas-abrahamsson/gpb",
{branch, "master"}}}
]}.
{proto_compiler, gpb}.
{gpb_opts, [{module_name_suffix, "_gpb"}]}.

View file

@ -0,0 +1,19 @@
// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// ex: ts=4 sw=4 et
package test3;
service test3
{
rpc testRpc3(RPC_INPUT3) returns (RPC_OUTPUT3);
}
message RPC_INPUT3
{
optional string str = 1;
}
message RPC_OUTPUT3
{
optional string str = 1;
}

View file

@ -0,0 +1,19 @@
// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// ex: ts=4 sw=4 et
package test2;
service test2
{
rpc testRpc2(RPC_INPUT2) returns (RPC_OUTPUT2);
}
message RPC_INPUT2
{
optional string str = 1;
}
message RPC_OUTPUT2
{
optional string str = 1;
}

View file

@ -0,0 +1,19 @@
// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// ex: ts=4 sw=4 et
package test5;
service test5
{
rpc testRpc5(RPC_INPUT5) returns (RPC_OUTPUT5);
}
message RPC_INPUT5
{
optional string str = 1;
}
message RPC_OUTPUT5
{
optional string str = 1;
}

View file

@ -0,0 +1,19 @@
// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// ex: ts=4 sw=4 et
package test4;
service test4
{
rpc testRpc4(RPC_INPUT4) returns (RPC_OUTPUT4);
}
message RPC_INPUT4
{
optional string str = 1;
}
message RPC_OUTPUT4
{
optional string str = 1;
}

View file

@ -0,0 +1,39 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(foo).
-export([start_link/0,
start_link/1,
init/1,
terminate/2,
handle_info/2,
handle_call/3,
handle_cast/2,
code_change/3]).
-behavior(gen_server).
-include("../include/test_gpb.hrl").
-include("../include/test2_gpb.hrl").
-include("../include/test3_gpb.hrl").
-include("../include/test4_gpb.hrl").
-include("../include/test5_gpb.hrl").
-record(state, {node :: node()}).
start_link() -> start_link(undefined).
start_link(Args) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
init(_Args) -> {ok, #state{node=node()}}.
terminate(_Reason, _Data) -> ok.
handle_info(_Info, State) -> {noreply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_call(_Msg, _From, State) -> {reply, ok, State}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.

View file

@ -0,0 +1,12 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(foo_app).
-behaviour(application).
-export([start/2,
stop/1]).
start(_Type, _Args) -> foo_sup:start_link().
stop(_State) -> ok.

View file

@ -0,0 +1,15 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(foo_sup).
-behavior(supervisor).
-export([start_link/0,
init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_Args) ->
FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]},
{ok,{{one_for_all,1,1}, [FooChild]}}.

View file

@ -0,0 +1,19 @@
// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// ex: ts=4 sw=4 et
package test;
service test
{
rpc testRpc(RPC_INPUT) returns (RPC_OUTPUT);
}
message RPC_INPUT
{
optional string str = 1;
}
message RPC_OUTPUT
{
optional string str = 1;
}

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,79 @@
%% -*- 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(proto_protobuffs_rt).
-export([files/0,
run/1]).
-include_lib("eunit/include/eunit.hrl").
-define(MODULES,
[foo,
foo_app,
foo_sup,
test_pb]).
-define(BEAM_FILES,
["foo.beam",
"foo_app.beam",
"foo_sup.beam",
"test_pb.beam"]).
files() ->
[
{copy, "../../rebar", "rebar"},
{copy, "rebar.config", "rebar.config"},
{copy, "include", "include"},
{copy, "src", "src"},
{create, "ebin/foo.app", app(foo, ?MODULES)}
].
run(_Dir) ->
?assertMatch({ok, _}, retest_sh:run("./rebar prepare-deps", [])),
?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])),
?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
ok = check_beams_generated(),
ok.
check_beams_generated() ->
lists:foreach(
fun(F) ->
File = filename:join("ebin", F),
?assert(filelib:is_regular(File))
end,
?BEAM_FILES).
%%
%% Generate the contents of a simple .app file
%%
app(Name, Modules) ->
App = {application, Name,
[{description, atom_to_list(Name)},
{vsn, "1"},
{modules, Modules},
{registered, []},
{applications, [kernel, stdlib, gpb]}]},
io_lib:format("~p.\n", [App]).

View file

@ -0,0 +1,18 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
{erl_opts,
[
{i, "deps/protobuffs/include"},
{platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'}
]}.
{deps,
[
{protobuffs, [], {git, "git://github.com/basho/erlang_protobuffs",
{branch, "master"}}}
]}.
%% The default proto compiler is protobuffs
%% so don't need to specify this
%% {proto_compiler, gpb}.

View file

@ -0,0 +1,35 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(foo).
-export([start_link/0,
start_link/1,
init/1,
terminate/2,
handle_info/2,
handle_call/3,
handle_cast/2,
code_change/3]).
-behavior(gen_server).
-include("../include/test_pb.hrl").
-record(state, {node :: node()}).
start_link() -> start_link(undefined).
start_link(Args) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
init(_Args) -> {ok, #state{node=node()}}.
terminate(_Reason, _Data) -> ok.
handle_info(_Info, State) -> {noreply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_call(_Msg, _From, State) -> {reply, ok, State}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.

View file

@ -0,0 +1,12 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(foo_app).
-behaviour(application).
-export([start/2,
stop/1]).
start(_Type, _Args) -> foo_sup:start_link().
stop(_State) -> ok.

View file

@ -0,0 +1,15 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(foo_sup).
-behavior(supervisor).
-export([start_link/0,
init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_Args) ->
FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]},
{ok,{{one_for_all,1,1}, [FooChild]}}.

View file

@ -0,0 +1,12 @@
// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// ex: ts=4 sw=4 et
message m1
{
optional string str = 1;
}
message m2
{
optional string str = 1;
}

View file

@ -25,6 +25,7 @@
- (\"lfe_comp\":\"file\"/\"2\") - (\"lfe_comp\":\"file\"/\"2\")
- (\"neotoma\":\"file\"/\"2\") - (\"neotoma\":\"file\"/\"2\")
- (\"protobuffs_compile\":\"scan_file\"/\"2\") - (\"protobuffs_compile\":\"scan_file\"/\"2\")
- (\"gpb_compile\":\"file\"/\"2\")
- (\"diameter_codegen\":\"from_dict\"/\"4\") - (\"diameter_codegen\":\"from_dict\"/\"4\")
- (\"diameter_dict_util\":\"format_error\"/\"1\") - (\"diameter_dict_util\":\"format_error\"/\"1\")
- (\"diameter_dict_util\":\"parse\"/\"2\"))", - (\"diameter_dict_util\":\"parse\"/\"2\"))",

View file

@ -89,6 +89,16 @@
%% Options for the ErlyDTL compiler %% Options for the ErlyDTL compiler
{erlydtl_opts, []}. {erlydtl_opts, []}.
%% == Proto compiler ==
{proto_compiler, protobuffs}.
%% Available compilers for protocol buffer files (*.proto):
%% protobuffs (default)
%% gpb
%% Options for the gpb protocol buffer compiler,
%% if selected by the proto_compiler option
{gpb_opts, []}.
%% == EUnit == %% == EUnit ==
%% Options for eunit:test() %% Options for eunit:test()

View file

@ -0,0 +1,144 @@
%% -*- 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
%% ===================================================================
compile(Config, AppFile) ->
case rebar_utils:find_files("src", "^[^._].*\\.proto$") of
[] ->
ok;
Protos ->
%% Find a compiler for protocol buffers,
%% use that for compiling protocol buffers
CompilerModule = select_proto_compiler(Config),
CompilerModule:proto_compile(Config, AppFile, Protos)
end.
clean(Config, AppFile) ->
%% Get a list of generated .beam and .hrl files and then delete them
Protos = rebar_utils:find_files("src", "^[^._].*\\.proto$"),
case Protos of
[] ->
ok;
_ ->
%% Ask the proto compiler to compile the .proto files.
CompilerModule = select_proto_compiler(Config),
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_compiler, Compiler}~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(Config) ->
Default = get_default_compiler(),
Key = rebar_config:get_local(Config, proto_compiler, Default),
AvailCompilers = find_proto_compilers(),
case lists:keyfind(Key, #proto_compiler.key, AvailCompilers) of
#proto_compiler{module=CompilerModule} ->
CompilerModule;
false ->
?ABORT("No such protocol buffer compiler known, '~s'~n"
++ "The following are known:~n"
++ "~s~n",
[Key, format_proto_compiler_list()])
end.
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)]).

View file

@ -0,0 +1,119 @@
%% -*- 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_gpb_compiler).
-export([key/0,
proto_compile/3,
proto_clean/3]).
%% for internal use only
-export([proto_info/2]).
-include("rebar.hrl").
%% ===================================================================
%% Public API
%% ===================================================================
key() ->
gpb.
proto_compile(Config, _AppFile, _ProtoFiles) ->
%% Check for gpb library -- if it's not present, fail
%% since we have.proto files that need building
case gpb_is_present() of
true ->
rebar_base_compiler:run(Config, [],
"src", ".proto",
"src", ".erl",
fun compile_gpb/3,
[{check_last_mod, true}]);
false ->
?ERROR("The gpb library is not present in code path!\n", []),
?FAIL
end.
proto_clean(Config, _AppFile, ProtoFiles) ->
GpbOpts = gpb_opts(Config),
MPrefix = proplists:get_value(module_name_prefix, GpbOpts, ""),
MSuffix = proplists:get_value(module_name_suffix, GpbOpts, ""),
rebar_file_utils:delete_each(
[beam_relpath(MPrefix, F, MSuffix) || F <- ProtoFiles]
++ [erl_relpath(MPrefix, F, MSuffix) || F <- ProtoFiles]
++ [hrl_relpath(MPrefix, F, MSuffix) || F <- ProtoFiles]),
ok.
%% ===================================================================
%% Internal functions
%% ===================================================================
proto_info(help, compile) ->
?CONSOLE(
" gpb_opts is passed as options to gpb_compile:file/2.~n"
" erl_opts is used when compiling the generated erlang files,~n"
" so you might want to add an include path to gpb here,~n"
" for gpb.hrl, or else use the include_as_lib gpb_opts option,~n"
" or the defs_as_proplists gpb_opts option.~n",
[]);
proto_info(help, clean) ->
?CONSOLE("", []).
gpb_opts(Config) ->
rebar_config:get_local(Config, gpb_opts, []).
gpb_is_present() ->
code:which(gpb) =/= non_existing.
compile_gpb(Source, _Target, Config) ->
SourceFullPath = filename:absname(Source),
DefaultDestOpts = [{o_erl, "src"}, {o_hrl, "include"}],
SelfIncludeOpt = [{i,filename:dirname(SourceFullPath)}],
GpbOpts = gpb_opts(Config) ++ DefaultDestOpts ++ SelfIncludeOpt,
ok = filelib:ensure_dir(filename:join("ebin", "dummy")),
ok = filelib:ensure_dir(filename:join("include", "dummy")),
?CONSOLE("Compiling ~s\n", [Source]),
case gpb_compile:file(SourceFullPath, GpbOpts) of
ok ->
ok;
{error, _Reason} ->
?ERROR("Failed to compile ~s~n", [Source]),
?FAIL
end.
beam_relpath(Prefix, Proto, Suffix) ->
proto_filename_to_relpath("ebin", Prefix, Proto, Suffix, ".beam").
erl_relpath(Prefix, Proto, Suffix) ->
proto_filename_to_relpath("src", Prefix, Proto, Suffix, ".erl").
hrl_relpath(Prefix, Proto, Suffix) ->
proto_filename_to_relpath("include", Prefix, Proto, Suffix, ".hrl").
proto_filename_to_relpath(Dir, Prefix, Proto, Suffix, NewExt) ->
BaseNoExt = filename:basename(Proto, ".proto"),
filename:join([Dir, Prefix ++ BaseNoExt ++ Suffix ++ NewExt]).

View file

@ -26,11 +26,12 @@
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
-module(rebar_protobuffs_compiler). -module(rebar_protobuffs_compiler).
-export([compile/2, -export([key/0,
clean/2]). proto_compile/3,
proto_clean/3]).
%% for internal use only %% for internal use only
-export([info/2]). -export([proto_info/2]).
-include("rebar.hrl"). -include("rebar.hrl").
@ -38,58 +39,48 @@
%% Public API %% Public API
%% =================================================================== %% ===================================================================
compile(Config, _AppFile) -> key() ->
case rebar_utils:find_files("src", "^[^._].*\\.proto$") of protobuffs.
[] ->
ok;
FoundFiles ->
%% Check for protobuffs library -- if it's not present, fail
%% since we have.proto files that need building
case protobuffs_is_present() of
true ->
%% Build a list of output files - { Proto, Beam, Hrl }
Targets = [{Proto, beam_file(Proto), hrl_file(Proto)} ||
Proto <- FoundFiles],
%% Compile each proto file proto_compile(Config, _AppFile, ProtoFiles) ->
compile_each(Config, Targets); %% Check for protobuffs library -- if it's not present, fail
false -> %% since we have.proto files that need building
?ERROR("Protobuffs library not present in code path!\n", case protobuffs_is_present() of
[]), true ->
?FAIL %% Build a list of output files - { Proto, Beam, Hrl }
end Targets = [{Proto, beam_file(Proto), hrl_file(Proto)} ||
Proto <- ProtoFiles],
%% Compile each proto file
compile_each(Config, Targets);
false ->
?ERROR("Protobuffs library not present in code path!\n",
[]),
?FAIL
end. end.
clean(_Config, _AppFile) -> proto_clean(_Config, _AppFile, ProtoFiles) ->
%% Get a list of generated .beam and .hrl files and then delete them %% Get a list of generated .beam and .hrl files and then delete them
Protos = rebar_utils:find_files("src", ".*\\.proto$"), BeamFiles = [fq_beam_file(F) || F <- ProtoFiles],
BeamFiles = [fq_beam_file(F) || F <- Protos], HrlFiles = [fq_hrl_file(F) || F <- ProtoFiles],
HrlFiles = [fq_hrl_file(F) || F <- Protos],
Targets = BeamFiles ++ HrlFiles, Targets = BeamFiles ++ HrlFiles,
case Targets of delete_each(Targets).
[] ->
ok;
_ ->
delete_each(Targets)
end.
%% =================================================================== %% ===================================================================
%% Internal functions %% Internal functions
%% =================================================================== %% ===================================================================
info(help, compile) -> proto_info(help, compile) ->
info_help("Build Protobuffs (*.proto) sources"); info_help();
info(help, clean) -> proto_info(help, clean) ->
info_help("Delete Protobuffs (*.proto) build results"). info_help().
info_help(Description) -> info_help() ->
?CONSOLE( ?CONSOLE(
"~s.~n"
"~n"
"Valid rebar.config options:~n" "Valid rebar.config options:~n"
" erl_opts is passed as compile_flags to " " erl_opts is passed as compile_flags to "
"protobuffs_compile:scan_file/2~n", "protobuffs_compile:scan_file/2~n",
[Description]). []).
protobuffs_is_present() -> protobuffs_is_present() ->
code:which(protobuffs_compile) =/= non_existing. code:which(protobuffs_compile) =/= non_existing.