From 0caf047fc3d978189e12dc398d06472eb3d236ad Mon Sep 17 00:00:00 2001 From: Tomas Abrahamsson Date: Tue, 17 Dec 2013 12:59:15 +0100 Subject: [PATCH 1/4] Introduce pluggable protocol buffer compilers Make it possible for plug in alternative protocol buffer compilers. The compilers are picked up based on if they export all of the functions key/0, proto_compile/3, proto_clean/3 and proto_info/2. The set of compiler modules to choose from, is fetched from the rebar application environment, from the app_dir modules. A new config option, {proto_compiler,Compiler}, specifies which of the available protocol buffer compilers to use. The 'protobuffs' compiler is now one such compiler (the only one), and it is also the default, for backwards compatibility. --- ebin/rebar.app | 2 + rebar.config.sample | 6 ++ src/rebar_proto_compiler.erl | 144 ++++++++++++++++++++++++++++++ src/rebar_protobuffs_compiler.erl | 71 +++++++-------- 4 files changed, 183 insertions(+), 40 deletions(-) create mode 100644 src/rebar_proto_compiler.erl diff --git a/ebin/rebar.app b/ebin/rebar.app index 55454e9..f92f574 100644 --- a/ebin/rebar.app +++ b/ebin/rebar.app @@ -28,6 +28,7 @@ rebar_neotoma_compiler, rebar_otp_app, rebar_port_compiler, + rebar_proto_compiler, rebar_protobuffs_compiler, rebar_qc, rebar_rel_utils, @@ -67,6 +68,7 @@ {modules, [ {app_dir, [ rebar_abnfc_compiler, + rebar_proto_compiler, rebar_protobuffs_compiler, rebar_neotoma_compiler, rebar_asn1_compiler, diff --git a/rebar.config.sample b/rebar.config.sample index d154ac1..d2a7869 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -89,6 +89,12 @@ %% Options for the ErlyDTL compiler {erlydtl_opts, []}. +%% == Proto compiler == +{proto_compiler, protobuffs}. +%% Available compilers for protocol buffer files (*.proto): +%% protobuffs (default) + + %% == EUnit == %% Options for eunit:test() diff --git a/src/rebar_proto_compiler.erl b/src/rebar_proto_compiler.erl new file mode 100644 index 0000000..c86dda2 --- /dev/null +++ b/src/rebar_proto_compiler.erl @@ -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)]). diff --git a/src/rebar_protobuffs_compiler.erl b/src/rebar_protobuffs_compiler.erl index e89c700..2b0b621 100644 --- a/src/rebar_protobuffs_compiler.erl +++ b/src/rebar_protobuffs_compiler.erl @@ -26,11 +26,12 @@ %% ------------------------------------------------------------------- -module(rebar_protobuffs_compiler). --export([compile/2, - clean/2]). +-export([key/0, + proto_compile/3, + proto_clean/3]). %% for internal use only --export([info/2]). +-export([proto_info/2]). -include("rebar.hrl"). @@ -38,58 +39,48 @@ %% Public API %% =================================================================== -compile(Config, _AppFile) -> - case rebar_utils:find_files("src", "^[^._].*\\.proto$") of - [] -> - 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], +key() -> + protobuffs. - %% Compile each proto file - compile_each(Config, Targets); - false -> - ?ERROR("Protobuffs library not present in code path!\n", - []), - ?FAIL - end +proto_compile(Config, _AppFile, ProtoFiles) -> + %% 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 <- ProtoFiles], + + %% Compile each proto file + compile_each(Config, Targets); + false -> + ?ERROR("Protobuffs library not present in code path!\n", + []), + ?FAIL end. -clean(_Config, _AppFile) -> +proto_clean(_Config, _AppFile, ProtoFiles) -> %% 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 <- Protos], - HrlFiles = [fq_hrl_file(F) || F <- Protos], + BeamFiles = [fq_beam_file(F) || F <- ProtoFiles], + HrlFiles = [fq_hrl_file(F) || F <- ProtoFiles], Targets = BeamFiles ++ HrlFiles, - case Targets of - [] -> - ok; - _ -> - delete_each(Targets) - end. + delete_each(Targets). %% =================================================================== %% Internal functions %% =================================================================== -info(help, compile) -> - info_help("Build Protobuffs (*.proto) sources"); -info(help, clean) -> - info_help("Delete Protobuffs (*.proto) build results"). +proto_info(help, compile) -> + info_help(); +proto_info(help, clean) -> + info_help(). -info_help(Description) -> +info_help() -> ?CONSOLE( - "~s.~n" - "~n" "Valid rebar.config options:~n" " erl_opts is passed as compile_flags to " "protobuffs_compile:scan_file/2~n", - [Description]). + []). protobuffs_is_present() -> code:which(protobuffs_compile) =/= non_existing. From 1b367a8b24ad43edbf4734fb306c358d69f7aa40 Mon Sep 17 00:00:00 2001 From: Tomas Abrahamsson Date: Tue, 17 Dec 2013 15:36:58 +0100 Subject: [PATCH 2/4] Add support for compiling proto files using gpb This adds the config option {proto_compiler,gpb} for selecting gpb as the compiler for protocol buffer files. When gpb is used as compiler, it reads the gpb_opts config item for options. --- ebin/rebar.app | 2 + rebar.config | 1 + rebar.config.sample | 4 ++ src/rebar_proto_gpb_compiler.erl | 119 +++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/rebar_proto_gpb_compiler.erl diff --git a/ebin/rebar.app b/ebin/rebar.app index f92f574..6e3609b 100644 --- a/ebin/rebar.app +++ b/ebin/rebar.app @@ -30,6 +30,7 @@ rebar_port_compiler, rebar_proto_compiler, rebar_protobuffs_compiler, + rebar_proto_gpb_compiler, rebar_qc, rebar_rel_utils, rebar_reltool, @@ -70,6 +71,7 @@ rebar_abnfc_compiler, rebar_proto_compiler, rebar_protobuffs_compiler, + rebar_proto_gpb_compiler, rebar_neotoma_compiler, rebar_asn1_compiler, rebar_dia_compiler, diff --git a/rebar.config b/rebar.config index 1c62a55..9150e98 100644 --- a/rebar.config +++ b/rebar.config @@ -24,6 +24,7 @@ - (\"lfe_comp\":\"file\"/\"2\") - (\"neotoma\":\"file\"/\"2\") - (\"protobuffs_compile\":\"scan_file\"/\"2\") + - (\"gpb_compile\":\"file\"/\"2\") - (\"diameter_codegen\":\"from_dict\"/\"4\") - (\"diameter_dict_util\":\"format_error\"/\"1\") - (\"diameter_dict_util\":\"parse\"/\"2\"))", diff --git a/rebar.config.sample b/rebar.config.sample index d2a7869..107a02f 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -93,7 +93,11 @@ {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 == diff --git a/src/rebar_proto_gpb_compiler.erl b/src/rebar_proto_gpb_compiler.erl new file mode 100644 index 0000000..e6798ab --- /dev/null +++ b/src/rebar_proto_gpb_compiler.erl @@ -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]). + From 144cb8c1566ad3c0cbb9e96bc4cdec319a983737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Rasc=C3=A3o?= Date: Sat, 27 Sep 2014 11:59:05 +0100 Subject: [PATCH 3/4] Add proto compiler gpb inttest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit exercises rebar/gpb integration The bulk of these tests are written by Luis Rascão, hence he is the author of this commit. As the committer, I have cherry-picked his two commits 4c87bcd and ebb8182, from the feature/support_gpb_protobuf branch in the git://github.com/lrascao/rebar repo, and have slightly adapted it to fit this pluggable-proto-compilers-gpb branch. Update the THANKS file --- THANKS | 3 +- inttest/proto_gpb/include/.gitignore | 4 + inttest/proto_gpb/proto_gpb_rt.erl | 110 ++++++++++++++++++++++++++ inttest/proto_gpb/rebar.config | 18 +++++ inttest/proto_gpb/src/a/b/test3.proto | 19 +++++ inttest/proto_gpb/src/a/test2.proto | 19 +++++ inttest/proto_gpb/src/c/d/test5.proto | 19 +++++ inttest/proto_gpb/src/c/test4.proto | 19 +++++ inttest/proto_gpb/src/foo.erl | 39 +++++++++ inttest/proto_gpb/src/foo_app.erl | 12 +++ inttest/proto_gpb/src/foo_sup.erl | 15 ++++ inttest/proto_gpb/src/test.proto | 19 +++++ 12 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 inttest/proto_gpb/include/.gitignore create mode 100644 inttest/proto_gpb/proto_gpb_rt.erl create mode 100644 inttest/proto_gpb/rebar.config create mode 100644 inttest/proto_gpb/src/a/b/test3.proto create mode 100644 inttest/proto_gpb/src/a/test2.proto create mode 100644 inttest/proto_gpb/src/c/d/test5.proto create mode 100644 inttest/proto_gpb/src/c/test4.proto create mode 100644 inttest/proto_gpb/src/foo.erl create mode 100644 inttest/proto_gpb/src/foo_app.erl create mode 100644 inttest/proto_gpb/src/foo_sup.erl create mode 100644 inttest/proto_gpb/src/test.proto diff --git a/THANKS b/THANKS index 9b9a448..b5d19be 100644 --- a/THANKS +++ b/THANKS @@ -127,4 +127,5 @@ alisdair sullivan Alexander Verbitsky Andras Horvath Drew Varner -Roberto Aloi \ No newline at end of file +Roberto Aloi +Luis Rascao diff --git a/inttest/proto_gpb/include/.gitignore b/inttest/proto_gpb/include/.gitignore new file mode 100644 index 0000000..5e7d273 --- /dev/null +++ b/inttest/proto_gpb/include/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/inttest/proto_gpb/proto_gpb_rt.erl b/inttest/proto_gpb/proto_gpb_rt.erl new file mode 100644 index 0000000..aafc677 --- /dev/null +++ b/inttest/proto_gpb/proto_gpb_rt.erl @@ -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]). diff --git a/inttest/proto_gpb/rebar.config b/inttest/proto_gpb/rebar.config new file mode 100644 index 0000000..a002245 --- /dev/null +++ b/inttest/proto_gpb/rebar.config @@ -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"}]}. diff --git a/inttest/proto_gpb/src/a/b/test3.proto b/inttest/proto_gpb/src/a/b/test3.proto new file mode 100644 index 0000000..6e3372f --- /dev/null +++ b/inttest/proto_gpb/src/a/b/test3.proto @@ -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; +} diff --git a/inttest/proto_gpb/src/a/test2.proto b/inttest/proto_gpb/src/a/test2.proto new file mode 100644 index 0000000..6a2d1ac --- /dev/null +++ b/inttest/proto_gpb/src/a/test2.proto @@ -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; +} diff --git a/inttest/proto_gpb/src/c/d/test5.proto b/inttest/proto_gpb/src/c/d/test5.proto new file mode 100644 index 0000000..e94b3bc --- /dev/null +++ b/inttest/proto_gpb/src/c/d/test5.proto @@ -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; +} diff --git a/inttest/proto_gpb/src/c/test4.proto b/inttest/proto_gpb/src/c/test4.proto new file mode 100644 index 0000000..3e1de74 --- /dev/null +++ b/inttest/proto_gpb/src/c/test4.proto @@ -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; +} diff --git a/inttest/proto_gpb/src/foo.erl b/inttest/proto_gpb/src/foo.erl new file mode 100644 index 0000000..3fdee54 --- /dev/null +++ b/inttest/proto_gpb/src/foo.erl @@ -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}. diff --git a/inttest/proto_gpb/src/foo_app.erl b/inttest/proto_gpb/src/foo_app.erl new file mode 100644 index 0000000..49e6d4b --- /dev/null +++ b/inttest/proto_gpb/src/foo_app.erl @@ -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. diff --git a/inttest/proto_gpb/src/foo_sup.erl b/inttest/proto_gpb/src/foo_sup.erl new file mode 100644 index 0000000..dbef2b8 --- /dev/null +++ b/inttest/proto_gpb/src/foo_sup.erl @@ -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]}}. diff --git a/inttest/proto_gpb/src/test.proto b/inttest/proto_gpb/src/test.proto new file mode 100644 index 0000000..9b3cf59 --- /dev/null +++ b/inttest/proto_gpb/src/test.proto @@ -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; +} From 9f057f29c591c8db4d216ea416bee3b3722552a8 Mon Sep 17 00:00:00 2001 From: Tomas Abrahamsson Date: Sat, 18 Oct 2014 00:59:31 +0200 Subject: [PATCH 4/4] Add inttest for default proto compiler (protobuffs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This borrows heavily from the inttest for gpb, many thanks to Luis Rascão for providing a most useful example. --- inttest/proto_protobuffs/include/.gitignore | 4 + .../proto_protobuffs/proto_protobuffs_rt.erl | 79 +++++++++++++++++++ inttest/proto_protobuffs/rebar.config | 18 +++++ inttest/proto_protobuffs/src/foo.erl | 35 ++++++++ inttest/proto_protobuffs/src/foo_app.erl | 12 +++ inttest/proto_protobuffs/src/foo_sup.erl | 15 ++++ inttest/proto_protobuffs/src/test.proto | 12 +++ 7 files changed, 175 insertions(+) create mode 100644 inttest/proto_protobuffs/include/.gitignore create mode 100644 inttest/proto_protobuffs/proto_protobuffs_rt.erl create mode 100644 inttest/proto_protobuffs/rebar.config create mode 100644 inttest/proto_protobuffs/src/foo.erl create mode 100644 inttest/proto_protobuffs/src/foo_app.erl create mode 100644 inttest/proto_protobuffs/src/foo_sup.erl create mode 100644 inttest/proto_protobuffs/src/test.proto diff --git a/inttest/proto_protobuffs/include/.gitignore b/inttest/proto_protobuffs/include/.gitignore new file mode 100644 index 0000000..5e7d273 --- /dev/null +++ b/inttest/proto_protobuffs/include/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/inttest/proto_protobuffs/proto_protobuffs_rt.erl b/inttest/proto_protobuffs/proto_protobuffs_rt.erl new file mode 100644 index 0000000..bd03033 --- /dev/null +++ b/inttest/proto_protobuffs/proto_protobuffs_rt.erl @@ -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]). diff --git a/inttest/proto_protobuffs/rebar.config b/inttest/proto_protobuffs/rebar.config new file mode 100644 index 0000000..2158d17 --- /dev/null +++ b/inttest/proto_protobuffs/rebar.config @@ -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}. diff --git a/inttest/proto_protobuffs/src/foo.erl b/inttest/proto_protobuffs/src/foo.erl new file mode 100644 index 0000000..46b7a60 --- /dev/null +++ b/inttest/proto_protobuffs/src/foo.erl @@ -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}. diff --git a/inttest/proto_protobuffs/src/foo_app.erl b/inttest/proto_protobuffs/src/foo_app.erl new file mode 100644 index 0000000..49e6d4b --- /dev/null +++ b/inttest/proto_protobuffs/src/foo_app.erl @@ -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. diff --git a/inttest/proto_protobuffs/src/foo_sup.erl b/inttest/proto_protobuffs/src/foo_sup.erl new file mode 100644 index 0000000..dbef2b8 --- /dev/null +++ b/inttest/proto_protobuffs/src/foo_sup.erl @@ -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]}}. diff --git a/inttest/proto_protobuffs/src/test.proto b/inttest/proto_protobuffs/src/test.proto new file mode 100644 index 0000000..e28e6e0 --- /dev/null +++ b/inttest/proto_protobuffs/src/test.proto @@ -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; +}