summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Abrahamsson <tomas.abrahamsson@gmail.com>2014-12-31 03:51:58 +0100
committerTomas Abrahamsson <tomas.abrahamsson@gmail.com>2015-01-24 22:11:04 +0100
commit1ce7059adb5db994c2a1a0c30ea9a34ff8aefc5e (patch)
treee99f3d431b85b3b09cd1c8a9a8ce394d3d42deb8
parent2e9706fd6156a358b7a6ed3c599a9d303212cc14 (diff)
downloadrebar-1ce7059adb5db994c2a1a0c30ea9a34ff8aefc5e.tar.gz
Recompile .proto files with gpb also with prefix/suffix
For protocol buffer files, when there were gpb options to alter the module name with prefix or suffix, recompilation was not properly detected. This is now fixed. (Issue #384). Use the rebar_base_compiler's ability to specify both source and target file names, to be able to also support prefixes. This also introduces a call to gpb_compile:format_error, so the xref recipe needs to be updated to ignore it, to avoid false errors.
-rw-r--r--inttest/proto_gpb/proto_gpb_rt.erl50
-rw-r--r--rebar.config1
-rw-r--r--src/rebar_proto_compiler.erl4
-rw-r--r--src/rebar_proto_gpb_compiler.erl73
4 files changed, 102 insertions, 26 deletions
diff --git a/inttest/proto_gpb/proto_gpb_rt.erl b/inttest/proto_gpb/proto_gpb_rt.erl
index 2cc5052..785922a 100644
--- a/inttest/proto_gpb/proto_gpb_rt.erl
+++ b/inttest/proto_gpb/proto_gpb_rt.erl
@@ -29,6 +29,8 @@
run/1]).
-include_lib("eunit/include/eunit.hrl").
+-include_lib("kernel/include/file.hrl").
+-include_lib("deps/retest/include/retest.hrl").
-define(MODULES,
[foo,
@@ -42,6 +44,13 @@
test4_gpb,
test5_gpb]).
+-define(SOURCE_PROTO_FILES,
+ ["test.proto",
+ "a/test2.proto",
+ "a/b/test3.proto",
+ "c/test4.proto",
+ "c/d/test5.proto"]).
+
files() ->
[
{copy, "../../rebar", "rebar"},
@@ -60,6 +69,17 @@ run(_Dir) ->
%% generating the test_gpb.hrl file, and also that it generated
%% the .hrl file was generated before foo was compiled.
ok = check_beams_generated(),
+
+ ?DEBUG("Verifying recompilation~n", []),
+ TestErl = hd(generated_erl_files()),
+ TestProto = hd(source_proto_files()),
+ make_proto_newer_than_erl(TestProto, TestErl),
+ TestMTime1 = read_mtime(TestErl),
+ ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
+ TestMTime2 = read_mtime(TestErl),
+ ?assert(TestMTime2 > TestMTime1),
+
+ ?DEBUG("Verify cleanup~n", []),
?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])),
ok = check_files_deleted(),
ok.
@@ -81,6 +101,12 @@ generated_erl_files() ->
generated_hrl_files() ->
add_dir("include", add_ext(?GENERATED_MODULES, ".hrl")).
+generated_beam_files() ->
+ add_dir("ebin", add_ext(?GENERATED_MODULES, ".beam")).
+
+source_proto_files() ->
+ add_dir("src", ?SOURCE_PROTO_FILES).
+
file_does_not_exist(F) ->
not filelib:is_regular(F).
@@ -90,6 +116,30 @@ add_ext(Modules, Ext) ->
add_dir(Dir, Files) ->
[filename:join(Dir, File) || File <- Files].
+read_mtime(File) ->
+ {ok, #file_info{mtime=MTime}} = file:read_file_info(File),
+ MTime.
+
+
+make_proto_newer_than_erl(Proto, Erl) ->
+ %% Do this by back-dating the erl file instead of touching the
+ %% proto file. Do this instead of sleeping for a second to get a
+ %% reliable test. Sleeping would have been needed sin ce the
+ %% #file_info{} (used by eg. filelib:last_modified) does not have
+ %% sub-second resolution (even though most file systems have).
+ {ok, #file_info{mtime=ProtoMTime}} = file:read_file_info(Proto),
+ {ok, ErlInfo} = file:read_file_info(Erl),
+ OlderMTime = update_seconds_to_datetime(ProtoMTime, -2),
+ OlderErlInfo = ErlInfo#file_info{mtime = OlderMTime},
+ ok = file:write_file_info(Erl, OlderErlInfo).
+
+update_seconds_to_datetime(DT, ToAdd) ->
+ calendar:gregorian_seconds_to_datetime(
+ calendar:datetime_to_gregorian_seconds(DT) + ToAdd).
+
+touch_file(File) ->
+ ?assertMatch({ok, _}, retest_sh:run("touch " ++ File, [])).
+
check(Check, Files) ->
lists:foreach(
fun(F) ->
diff --git a/rebar.config b/rebar.config
index d90e1f1..33d2aea 100644
--- a/rebar.config
+++ b/rebar.config
@@ -26,6 +26,7 @@
- (\"neotoma\":\"file\"/\"2\")
- (\"protobuffs_compile\":\"scan_file\"/\"2\")
- (\"gpb_compile\":\"file\"/\"2\")
+ - (\"gpb_compile\":\"format_error\"/\"1\")
- (\"diameter_codegen\":\"from_dict\"/\"4\")
- (\"diameter_dict_util\":\"format_error\"/\"1\")
- (\"diameter_dict_util\":\"parse\"/\"2\"))",
diff --git a/src/rebar_proto_compiler.erl b/src/rebar_proto_compiler.erl
index bed9ced..2d3eb2b 100644
--- a/src/rebar_proto_compiler.erl
+++ b/src/rebar_proto_compiler.erl
@@ -44,7 +44,7 @@
%% ===================================================================
compile(Config, AppFile) ->
- case rebar_utils:find_files_by_ext("src", ".proto", true) of
+ case rebar_utils:find_files_by_ext("src", ".proto") of
[] ->
ok;
Protos ->
@@ -56,7 +56,7 @@ compile(Config, AppFile) ->
clean(Config, AppFile) ->
%% Get a list of generated .beam and .hrl files and then delete them
- Protos = rebar_utils:find_files_by_ext("src", ".proto", true),
+ Protos = rebar_utils:find_files_by_ext("src", ".proto"),
case Protos of
[] ->
ok;
diff --git a/src/rebar_proto_gpb_compiler.erl b/src/rebar_proto_gpb_compiler.erl
index 32a1f6d..37f901c 100644
--- a/src/rebar_proto_gpb_compiler.erl
+++ b/src/rebar_proto_gpb_compiler.erl
@@ -47,9 +47,12 @@ proto_compile(Config, _AppFile, _ProtoFiles) ->
%% since we have.proto files that need building
case gpb_is_present() of
true ->
+ GpbOpts = user_gpb_opts(Config),
+ Files = rebar_utils:find_files_by_ext("src", ".proto"),
+ Targets = [filename:join("src", target_filename(F, GpbOpts))
+ || F <- Files],
rebar_base_compiler:run(Config, [],
- "src", ".proto",
- "src", ".erl",
+ lists:zip(Files, Targets),
fun compile_gpb/3,
[{check_last_mod, true}]);
false ->
@@ -57,14 +60,18 @@ proto_compile(Config, _AppFile, _ProtoFiles) ->
?FAIL
end.
+target_filename(ProtoFileName, GpbOpts) ->
+ ModulePrefix = proplists:get_value(module_name_prefix, GpbOpts, ""),
+ ModuleSuffix = proplists:get_value(module_name_suffix, GpbOpts, ""),
+ Base = filename:basename(ProtoFileName, ".proto"),
+ ModulePrefix ++ Base ++ ModuleSuffix ++ ".erl".
+
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, ""),
+ GpbOpts = user_gpb_opts(Config) ++ default_dest_opts(),
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]),
+ [beam_file(F, GpbOpts) || F <- ProtoFiles]
+ ++ [erl_file(F, GpbOpts) || F <- ProtoFiles]
+ ++ [hrl_file(F, GpbOpts) || F <- ProtoFiles]),
ok.
%% ===================================================================
@@ -82,37 +89,55 @@ proto_info(help, compile) ->
proto_info(help, clean) ->
?CONSOLE("", []).
-gpb_opts(Config) ->
- rebar_config:get_local(Config, gpb_opts, []).
-
gpb_is_present() ->
code:which(gpb) =/= non_existing.
+user_gpb_opts(Config) ->
+ rebar_config:get_local(Config, gpb_opts, []).
+
+default_dest_opts() ->
+ [{o_erl, "src"}, {o_hrl, "include"}].
+
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,
+ GpbOpts = user_gpb_opts(Config) ++ default_dest_opts()
+ ++ default_include_opts(SourceFullPath),
ok = filelib:ensure_dir(filename:join("ebin", "dummy")),
ok = filelib:ensure_dir(filename:join("include", "dummy")),
case gpb_compile:file(SourceFullPath, GpbOpts) of
ok ->
ok;
- {error, _Reason} ->
- ?ERROR("Failed to compile ~s~n", [Source]),
+ {error, Reason} ->
+ ReasonStr = gpb_compile:format_error(Reason),
+ ?ERROR("Failed to compile ~s: ~s~n", [SourceFullPath, ReasonStr]),
?FAIL
end.
-beam_relpath(Prefix, Proto, Suffix) ->
- proto_filename_to_relpath("ebin", Prefix, Proto, Suffix, ".beam").
+default_include_opts(SourceFullPath) ->
+ [{i,filename:dirname(SourceFullPath)}].
+
+beam_file(ProtoFile, GpbOpts) ->
+ proto_filename_to_path("ebin", ProtoFile, ".beam", GpbOpts).
-erl_relpath(Prefix, Proto, Suffix) ->
- proto_filename_to_relpath("src", Prefix, Proto, Suffix, ".erl").
+erl_file(ProtoFile, GpbOpts) ->
+ ErlOutDir = get_erl_outdir(GpbOpts),
+ proto_filename_to_path(ErlOutDir, ProtoFile, ".erl", GpbOpts).
-hrl_relpath(Prefix, Proto, Suffix) ->
- proto_filename_to_relpath("include", Prefix, Proto, Suffix, ".hrl").
+hrl_file(ProtoFile, GpbOpts) ->
+ HrlOutDir = get_hrl_outdir(GpbOpts),
+ proto_filename_to_path(HrlOutDir, ProtoFile, ".hrl", GpbOpts).
-proto_filename_to_relpath(Dir, Prefix, Proto, Suffix, NewExt) ->
- BaseNoExt = filename:basename(Proto, ".proto"),
+proto_filename_to_path(Dir, ProtoFile, NewExt, GpbOpts) ->
+ BaseNoExt = filename:basename(ProtoFile, ".proto"),
+ Prefix = proplists:get_value(module_name_prefix, GpbOpts, ""),
+ Suffix = proplists:get_value(module_name_suffix, GpbOpts, ""),
filename:join([Dir, Prefix ++ BaseNoExt ++ Suffix ++ NewExt]).
+get_erl_outdir(Opts) ->
+ proplists:get_value(o_erl, Opts, get_outdir(Opts)).
+
+get_hrl_outdir(Opts) ->
+ proplists:get_value(o_hrl, Opts, get_outdir(Opts)).
+
+get_outdir(Opts) ->
+ proplists:get_value(o, Opts, ".").