summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--Makefile3
-rw-r--r--THANKS4
-rwxr-xr-xbootstrap15
-rw-r--r--dialyzer_reference2
-rw-r--r--inttest/erlc_dep_graph/erlc_dep_graph_rt.erl111
-rw-r--r--inttest/erlc_dep_graph/extra_include/extra.hrl3
-rw-r--r--inttest/erlc_dep_graph/include/lambda.hrl3
-rw-r--r--inttest/erlc_dep_graph/rebar.config8
-rw-r--r--inttest/erlc_dep_graph/src/foo.app.src7
-rw-r--r--inttest/erlc_dep_graph/src/java.erl11
-rw-r--r--inttest/erlc_dep_graph/src/java.erl.no_extra10
-rw-r--r--inttest/erlc_dep_graph/src/lisp.erl13
-rw-r--r--inttest/erlc_dep_graph/src/pascal.erl8
-rw-r--r--inttest/erlc_dep_graph/src/perl.erl10
-rw-r--r--inttest/proto_gpb/mock/gpb/src/gpb_compile.erl5
-rw-r--r--inttest/proto_gpb/proto.bad/a/b/test3.proto19
-rw-r--r--inttest/proto_gpb/proto.bad/a/test2.proto19
-rw-r--r--inttest/proto_gpb/proto.bad/bad.proto19
-rw-r--r--inttest/proto_gpb/proto.bad/c/d/test5.proto19
-rw-r--r--inttest/proto_gpb/proto.bad/c/test4.proto19
-rw-r--r--inttest/proto_gpb/proto.bad/test.proto19
-rw-r--r--inttest/proto_gpb/proto_gpb_rt.erl23
-rw-r--r--inttest/proto_gpb/rebar.bad.config23
-rw-r--r--priv/templates/simplenode.windows.runner.cmd3
-rw-r--r--rebar.config.sample8
-rw-r--r--src/rebar_appups.erl2
-rw-r--r--src/rebar_base_compiler.erl19
-rw-r--r--src/rebar_dia_compiler.erl47
-rw-r--r--src/rebar_erlc_compiler.erl463
-rw-r--r--src/rebar_xref.erl3
31 files changed, 629 insertions, 292 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 78ebf09..968153e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -76,6 +76,9 @@ To do that, run `make check`. If you didn't build via `make debug` at first, the
and [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html), causing a test
failure.
If that happens, running `make clean` before running `make check` could solve the problem.
+
+Dialyzer requires a PLT (Persitent Lookup Table) to work with, and `make check` will fail without it. The PLT that rebar uses needs to initially be created with `make build_plt`, and is named based on the version of Erlang/OTP in use. See the [Dialyzer man page](http://www.erlang.org/doc/man/dialyzer.html) or [this further explanation](http://www.erlang.org/doc/apps/dialyzer/dialyzer_chapter.html) for additional information.
+
If you change any of the files with known but safe to ignore Dialyzer warnings, you may
have to adapt the line number(s) in [dialyzer_reference](dialyzer_reference). If you do that,
do not remove the
diff --git a/Makefile b/Makefile
index ac1cb7e..ed8dd48 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@ OTPVSNCMD='io:fwrite("~s",[rebar_utils:otp_release()]), halt().'
OTPVSN=$(shell erl -pa ebin/ -noshell -eval $(OTPVSNCMD))
PLT_FILENAME=~/.dialyzer_rebar_$(OTPVSN)_plt
LOG_LEVEL?=debug
+RT_TARGETS?=inttest
all:
./bootstrap
@@ -73,6 +74,6 @@ test_eunit: all
@$(REBAR) eunit
test_inttest: all deps
- @$(RETEST) -l $(LOG_LEVEL) inttest
+ @$(RETEST) -l $(LOG_LEVEL) $(RT_TARGETS)
travis: clean debug xref clean all deps test
diff --git a/THANKS b/THANKS
index 13e1cd7..a205df7 100644
--- a/THANKS
+++ b/THANKS
@@ -135,4 +135,8 @@ Pavel Baturko
Igor Savchuk
Mark Anderson
Brian H. Ward
+David Kubecka
+Carlos Eduardo de Paula
+Paulo F. Oliveira
+Derek Brown
Danil Onishchenko
diff --git a/bootstrap b/bootstrap
index 7d9a1c1..f02ccd3 100755
--- a/bootstrap
+++ b/bootstrap
@@ -121,9 +121,22 @@ rm(Path) ->
ok.
build_time() ->
- {{Y, M, D}, {H, Min, S}} = calendar:now_to_universal_time(now()),
+ {{Y, M, D}, {H, Min, S}} = calendar:now_to_universal_time(rebar_now()),
lists:flatten(io_lib:format("~4..0w~2..0w~2..0w_~2..0w~2..0w~2..0w",
[Y, M, D, H, Min, S])).
+rebar_now() ->
+ case erlang:function_exported(erlang, timestamp, 0) of
+ true ->
+ erlang:timestamp();
+ false ->
+ %% erlang:now/0 was deprecated in 18.0, and as the escript has to
+ %% pass erl_lint:module/1 (even without -mode(compile)), we would
+ %% see a deprecation warning for erlang:now/0. One solution is to
+ %% use -compile({nowarn_deprecated_function, [{erlang, now, 0}]}),
+ %% but that would raise a warning in versions older than 18.0.
+ %% Calling erlang:now/0 via apply/3 avoids that.
+ apply(erlang, now, [])
+ end.
vcs_info([]) ->
"No VCS info available.";
diff --git a/dialyzer_reference b/dialyzer_reference
index 9b7f093..41b1cff 100644
--- a/dialyzer_reference
+++ b/dialyzer_reference
@@ -1,3 +1,3 @@
rebar_eunit.erl:471: Call to missing or unexported function eunit_test:function_wrapper/2
-rebar_utils.erl:198: Call to missing or unexported function escript:foldl/3
+rebar_utils.erl:222: Call to missing or unexported function escript:foldl/3
diff --git a/inttest/erlc_dep_graph/erlc_dep_graph_rt.erl b/inttest/erlc_dep_graph/erlc_dep_graph_rt.erl
new file mode 100644
index 0000000..384ce87
--- /dev/null
+++ b/inttest/erlc_dep_graph/erlc_dep_graph_rt.erl
@@ -0,0 +1,111 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% Copyright (c) 2015 David Kubecka
+%%
+%% 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(erlc_dep_graph_rt).
+-export([files/0,
+ run/1]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+files() ->
+ [{copy, "../../rebar", "rebar"},
+ {copy, "rebar.config", "rebar.config"},
+ {copy, "src", "src"},
+ {copy, "include", "include"},
+ {copy, "extra_include", "extra_include"}].
+
+run(_Dir) ->
+ compile_all(ok, ""),
+ check_beams_ok(),
+ check_beams_untouched(filelib:wildcard("ebin/*.beam")),
+ modify_and_recompile_ok("src/lisp.erl", "ebin/lisp.beam"),
+
+ clean_all_ok(),
+ compile_all(error, "-C rebar.config.non-existing"),
+ compile_all(ok, ""),
+ modify_and_recompile_ok("extra_include/extra.hrl", "ebin/java.beam"),
+
+ Java = "src/java.erl",
+ {ok, OrigContent} = file:read_file(Java),
+ %% Remove header file inclusion
+ {ok, _} = file:copy("src/java.erl.no_extra", Java),
+ %% Ensure recompilation
+ touch([Java]),
+ compile_all(ok, ""),
+ %% Modify that header file
+ touch(["extra_include/extra.hrl"]),
+ %% Ensure we don't have to recompile anything
+ check_beams_untouched(["ebin/java.beam"]),
+ %% Clean up
+ ok = file:write_file(Java, OrigContent),
+
+ %% Check that changes propagate deeply through the dependency tree
+ modify_and_recompile_ok("include/lambda.hrl", "ebin/perl.beam"),
+
+ ok.
+
+check_beams_ok() ->
+ F = fun(BeamFile) -> ?assert(filelib:is_regular(BeamFile)) end,
+ with_erl_beams(F).
+
+check_beams_untouched(Beams) ->
+ compile_all_and_assert_mtimes(Beams, fun erlang:'=:='/2).
+
+modify_and_recompile_ok(TouchFile, CheckFile) ->
+ touch([TouchFile]),
+ compile_all_and_assert_mtimes([CheckFile], fun erlang:'<'/2).
+
+compile_all_and_assert_mtimes(Beams, Cmp) ->
+ BeamsModifiedBefore = mtime_ns(Beams),
+ compile_all(ok, ""),
+ BeamsModifiedAfter = mtime_ns(Beams),
+ lists:zipwith(fun(Before, After) -> ?assert(Cmp(Before, After)) end,
+ BeamsModifiedBefore, BeamsModifiedAfter).
+
+with_erl_beams(F) ->
+ lists:map(
+ fun(ErlFile) ->
+ ErlRoot = filename:rootname(filename:basename(ErlFile)),
+ BeamFile = filename:join("ebin", ErlRoot ++ ".beam"),
+ F(BeamFile)
+ end,
+ filelib:wildcard("src/*.erl")).
+
+mtime_ns(Files) ->
+ [os:cmd("stat -c%y " ++ File) || File <- Files].
+
+touch(Files) ->
+ %% Sleep one second so that filelib:last_modified/1 is guaranteed to notice
+ %% that files have changed.
+ ok = timer:sleep(1000),
+ [os:cmd("touch " ++ File) || File <- Files].
+
+compile_all(Result, Opts) ->
+ ?assertMatch({Result, _},
+ retest_sh:run("./rebar " ++ Opts ++ " compile", [])).
+
+clean_all_ok() ->
+ ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])).
diff --git a/inttest/erlc_dep_graph/extra_include/extra.hrl b/inttest/erlc_dep_graph/extra_include/extra.hrl
new file mode 100644
index 0000000..9d034c9
--- /dev/null
+++ b/inttest/erlc_dep_graph/extra_include/extra.hrl
@@ -0,0 +1,3 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-define(CONCISE, impossible).
diff --git a/inttest/erlc_dep_graph/include/lambda.hrl b/inttest/erlc_dep_graph/include/lambda.hrl
new file mode 100644
index 0000000..6b1622c
--- /dev/null
+++ b/inttest/erlc_dep_graph/include/lambda.hrl
@@ -0,0 +1,3 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-define(FUN, fake).
diff --git a/inttest/erlc_dep_graph/rebar.config b/inttest/erlc_dep_graph/rebar.config
new file mode 100644
index 0000000..857d349
--- /dev/null
+++ b/inttest/erlc_dep_graph/rebar.config
@@ -0,0 +1,8 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+{erl_opts,
+ [
+ {i, "extra_include"},
+ {parse_transform, lisp},
+ {parse_transform, pascal}
+ ]}.
diff --git a/inttest/erlc_dep_graph/src/foo.app.src b/inttest/erlc_dep_graph/src/foo.app.src
new file mode 100644
index 0000000..307b1bc
--- /dev/null
+++ b/inttest/erlc_dep_graph/src/foo.app.src
@@ -0,0 +1,7 @@
+{application,foo,
+ [{description,[]},
+ {vsn,"1.0.0"},
+ {registered,[]},
+ {applications,[kernel,stdlib]},
+ {env,[]}
+ ]}. \ No newline at end of file
diff --git a/inttest/erlc_dep_graph/src/java.erl b/inttest/erlc_dep_graph/src/java.erl
new file mode 100644
index 0000000..2e3f281
--- /dev/null
+++ b/inttest/erlc_dep_graph/src/java.erl
@@ -0,0 +1,11 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(java).
+
+-export([factory/0]).
+
+-include("lambda.hrl").
+-include("extra.hrl").
+
+factory() ->
+ ?FUN.
diff --git a/inttest/erlc_dep_graph/src/java.erl.no_extra b/inttest/erlc_dep_graph/src/java.erl.no_extra
new file mode 100644
index 0000000..7a8fc04
--- /dev/null
+++ b/inttest/erlc_dep_graph/src/java.erl.no_extra
@@ -0,0 +1,10 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(java).
+
+-export([factory/0]).
+
+-include("lambda.hrl").
+
+factory() ->
+ ?FUN.
diff --git a/inttest/erlc_dep_graph/src/lisp.erl b/inttest/erlc_dep_graph/src/lisp.erl
new file mode 100644
index 0000000..31fc8b5
--- /dev/null
+++ b/inttest/erlc_dep_graph/src/lisp.erl
@@ -0,0 +1,13 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(lisp).
+
+-export([parse_transform/2]).
+
+-include("lambda.hrl").
+-ifdef(NOT_DEFINED).
+-include_lib("include/non/existent.hrl").
+-endif.
+
+parse_transform(Forms, _Options) ->
+ Forms.
diff --git a/inttest/erlc_dep_graph/src/pascal.erl b/inttest/erlc_dep_graph/src/pascal.erl
new file mode 100644
index 0000000..e116034
--- /dev/null
+++ b/inttest/erlc_dep_graph/src/pascal.erl
@@ -0,0 +1,8 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(pascal).
+
+-export([parse_transform/2]).
+
+parse_transform(Forms, _Options) ->
+ Forms.
diff --git a/inttest/erlc_dep_graph/src/perl.erl b/inttest/erlc_dep_graph/src/perl.erl
new file mode 100644
index 0000000..9687948
--- /dev/null
+++ b/inttest/erlc_dep_graph/src/perl.erl
@@ -0,0 +1,10 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(perl).
+
+-export(['$_'/0]).
+
+-compile({parse_transform, lisp}).
+
+'$_'() ->
+ anything.
diff --git a/inttest/proto_gpb/mock/gpb/src/gpb_compile.erl b/inttest/proto_gpb/mock/gpb/src/gpb_compile.erl
index f7c4407..9692271 100644
--- a/inttest/proto_gpb/mock/gpb/src/gpb_compile.erl
+++ b/inttest/proto_gpb/mock/gpb/src/gpb_compile.erl
@@ -29,7 +29,12 @@
%% Simulate gpb compiling some proto files,
%% but generate only enough of what's needed for testing -- dummy stuff only.
+%% if a bad.proto file is supplied then gpb fails
file(Proto, Opts) ->
+ ok = case filename:basename(Proto) of
+ "bad.proto" -> error;
+ _ -> ok
+ end,
Prefix = proplists:get_value(module_name_prefix, Opts, ""),
Suffix = proplists:get_value(module_name_suffix, Opts, ""),
ProtoBase = filename:basename(Proto, ".proto"),
diff --git a/inttest/proto_gpb/proto.bad/a/b/test3.proto b/inttest/proto_gpb/proto.bad/a/b/test3.proto
new file mode 100644
index 0000000..6e3372f
--- /dev/null
+++ b/inttest/proto_gpb/proto.bad/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/proto.bad/a/test2.proto b/inttest/proto_gpb/proto.bad/a/test2.proto
new file mode 100644
index 0000000..6a2d1ac
--- /dev/null
+++ b/inttest/proto_gpb/proto.bad/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/proto.bad/bad.proto b/inttest/proto_gpb/proto.bad/bad.proto
new file mode 100644
index 0000000..ebe2274
--- /dev/null
+++ b/inttest/proto_gpb/proto.bad/bad.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
+ // bug introduced intentionally here
+ optional string str = 1;
+}
+
+message RPC_OUTPUT
+{
+ optional string str = 1;
+}
diff --git a/inttest/proto_gpb/proto.bad/c/d/test5.proto b/inttest/proto_gpb/proto.bad/c/d/test5.proto
new file mode 100644
index 0000000..e94b3bc
--- /dev/null
+++ b/inttest/proto_gpb/proto.bad/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/proto.bad/c/test4.proto b/inttest/proto_gpb/proto.bad/c/test4.proto
new file mode 100644
index 0000000..3e1de74
--- /dev/null
+++ b/inttest/proto_gpb/proto.bad/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/proto.bad/test.proto b/inttest/proto_gpb/proto.bad/test.proto
new file mode 100644
index 0000000..9b3cf59
--- /dev/null
+++ b/inttest/proto_gpb/proto.bad/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;
+}
diff --git a/inttest/proto_gpb/proto_gpb_rt.erl b/inttest/proto_gpb/proto_gpb_rt.erl
index 09fcde2..8a7cacf 100644
--- a/inttest/proto_gpb/proto_gpb_rt.erl
+++ b/inttest/proto_gpb/proto_gpb_rt.erl
@@ -56,20 +56,35 @@ files() ->
{copy, "../../rebar", "rebar"},
{copy, "rebar.config", "rebar.config"},
{copy, "rebar2.config", "rebar2.config"},
+ {copy, "rebar.bad.config", "rebar.bad.config"},
{copy, "include", "include"},
{copy, "src", "src"},
{copy, "proto", "proto"},
+ {copy, "proto.bad", "proto.bad"},
{copy, "mock", "deps"},
{create, "ebin/foo.app", app(foo, ?MODULES ++ ?GENERATED_MODULES)}
].
run(_Dir) ->
% perform test obtaining the .proto files from src dir
- ok = run_from_dir("src", "rebar.config"),
+ ok = run_from_dir(success_expected, "src", "rebar.config"),
% perform test obtaining the .proto files from proto dir
- ok = run_from_dir("proto", "rebar2.config").
-
-run_from_dir(ProtoDir, ConfigFile) ->
+ ok = run_from_dir(success_expected, "proto", "rebar2.config"),
+ % perform a test where a failure is expected
+ ok = run_from_dir(fail_expected, "proto.bad", "rebar.bad.config").
+
+run_from_dir(fail_expected, _ProtoDir, ConfigFile) ->
+ %% we expect a failure to happen, however rebar should not crash;
+ %% We make sure of that by scanning the error.
+ {error, {stopped, {1, Error}}} = retest_sh:run("./rebar --config "
+ ++ ConfigFile
+ ++ " compile",
+ []),
+ %% No matches of the string 'EXIT' should occur, these
+ %% indicate a rebar crash and not a exit with error.
+ 0 = string:str(lists:flatten(Error), "'EXIT'"),
+ ok;
+run_from_dir(success_expected, ProtoDir, ConfigFile) ->
?assertMatch({ok, _}, retest_sh:run("./rebar --config "
++ ConfigFile
++ " clean",
diff --git a/inttest/proto_gpb/rebar.bad.config b/inttest/proto_gpb/rebar.bad.config
new file mode 100644
index 0000000..a7ff5ae
--- /dev/null
+++ b/inttest/proto_gpb/rebar.bad.config
@@ -0,0 +1,23 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+
+{erl_opts,
+ [
+ {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'}
+ ]}.
+
+{deps,
+ [
+ %% The dependency below to gpb is needed for "rebar compile" to
+ %% work, thus for the inttest to work, but the gpb that is actually
+ %% used in inttest is brought in from the inttest/proto_gpb/mock
+ %% subdirectory.
+ {gpb, ".*"}
+ ]}.
+
+{proto_opts, [
+ {compiler, gpb},
+ {src_dirs, ["proto.bad"]}
+]}.
+
+{gpb_opts, [{module_name_suffix, "_gpb"}]}.
diff --git a/priv/templates/simplenode.windows.runner.cmd b/priv/templates/simplenode.windows.runner.cmd
index db3b054..d71a8c7 100644
--- a/priv/templates/simplenode.windows.runner.cmd
+++ b/priv/templates/simplenode.windows.runner.cmd
@@ -6,6 +6,9 @@
@rem which is assumed to be the node root.
@for /F "delims=" %%I in ("%~dp0..") do @set node_root=%%~fI
+@rem CWD to the node root directory
+@cd %node_root%
+
@set releases_dir=%node_root%\releases
@rem Parse ERTS version and release version from start_erl.data
diff --git a/rebar.config.sample b/rebar.config.sample
index 91d3dff..90ea6ee 100644
--- a/rebar.config.sample
+++ b/rebar.config.sample
@@ -104,6 +104,14 @@
%% if selected by the proto_compiler option
{gpb_opts, []}.
+%% == Diameter compiler ==
+
+%% Diameter files to compile before the rest
+{dia_first_files, []}.
+
+%% Options for the diameter compiler
+{dia_opts, []}.
+
%% == EUnit ==
%% Options for eunit:test()
diff --git a/src/rebar_appups.erl b/src/rebar_appups.erl
index 88ea705..8eefe56 100644
--- a/src/rebar_appups.erl
+++ b/src/rebar_appups.erl
@@ -54,7 +54,7 @@
%% Get the new and old release name and versions
{Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
- NewVerPath = filename:join([TargetParentDir, Name]),
+ NewVerPath = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
{NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NewVerPath),
{OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath),
diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl
index c38fb11..75ab490 100644
--- a/src/rebar_base_compiler.erl
+++ b/src/rebar_base_compiler.erl
@@ -129,21 +129,10 @@ remove_common_path1([Part | RestFilename], [Part | RestPath]) ->
remove_common_path1(FilenameParts, _) ->
filename:join(FilenameParts).
-
-compile(Unit, Config, CompileFn) ->
- case CompileFn(Unit, Config) of
- ok ->
- ok;
- skipped ->
- skipped;
- Error ->
- Error
- end.
-
compile_each([], _Config, _CompileFn) ->
ok;
compile_each([Unit | Rest], Config, CompileFn) ->
- case compile(Unit, Config, CompileFn) of
+ case CompileFn(Unit, Config) of
ok ->
?CONSOLE("Compiled ~s\n", [unit_source(Unit)]);
{ok, Warnings} ->
@@ -184,10 +173,10 @@ compile_queue(Config, Pids, Targets) ->
compile_queue(Config, Pids, Rest)
end;
- {fail, {_, {source, Source}}=Error} ->
+ {fail, {_, {source, Unit}}=Error} ->
maybe_report(Error),
?CONSOLE("Compiling ~s failed:\n",
- [maybe_absname(Config, Source)]),
+ [maybe_absname(Config, unit_source(Unit))]),
?DEBUG("Worker compilation failed: ~p\n", [Error]),
case rebar_config:get_xconf(Config, keep_going, false) of
false ->
@@ -224,7 +213,7 @@ compile_worker(QueuePid, Config, CompileFn) ->
QueuePid ! {next, self()},
receive
{compile, Source} ->
- case catch(compile(Source, Config, CompileFn)) of
+ case catch(CompileFn(Source, Config)) of
{ok, Ws} ->
QueuePid ! {compiled, Source, Ws},
compile_worker(QueuePid, Config, CompileFn);
diff --git a/src/rebar_dia_compiler.erl b/src/rebar_dia_compiler.erl
index 5ea84d7..56d5189 100644
--- a/src/rebar_dia_compiler.erl
+++ b/src/rebar_dia_compiler.erl
@@ -40,12 +40,26 @@
-spec compile(rebar_config:config(), file:filename()) -> 'ok'.
compile(Config, _AppFile) ->
- rebar_base_compiler:run(Config, filelib:wildcard("dia/*.dia"),
+ DiaOpts = rebar_config:get(Config, dia_opts, []),
+ IncludeEbin = proplists:get_value(include, DiaOpts, []),
+ DiaFiles = filelib:wildcard("dia/*.dia"),
+ code:add_pathsz(["ebin" | IncludeEbin]),
+ FileSequence = case rebar_config:get(Config, dia_first_files, []) of
+ [] ->
+ DiaFiles;
+ CompileFirst ->
+ CompileFirst ++
+ [F || F <- DiaFiles, not lists:member(F, CompileFirst)]
+ end,
+ rebar_base_compiler:run(Config, FileSequence,
"dia", ".dia", "src", ".erl",
fun compile_dia/3).
-spec clean(rebar_config:config(), file:filename()) -> 'ok'.
-clean(_Config, _AppFile) ->
+clean(Config, _AppFile) ->
+ DiaOpts = rebar_config:get(Config, dia_opts, []),
+ IncludeEbin = proplists:get_value(include, DiaOpts, []),
+ code:add_pathsz(["ebin" | IncludeEbin]),
GeneratedFiles = dia_generated_files("dia", "src", "include"),
ok = rebar_file_utils:delete_each(GeneratedFiles),
ok.
@@ -64,7 +78,9 @@ info_help(Description) ->
"~s.~n"
"~n"
"Valid rebar.config options:~n"
- " {dia_opts, []} (see diameter_codegen:from_dict/4 documentation)~n",
+ " {dia_opts, []} (options from diameter_make:codec/2 supported with~n"
+ " exception of inherits)~n"
+ " {dia_first_files, []} (files in sequence to compile first)~n",
[Description]).
-spec compile_dia(file:filename(), file:filename(),
@@ -79,6 +95,10 @@ compile_dia(Source, Target, Config) ->
_ = diameter_codegen:from_dict(FileName, Spec, Opts, erl),
_ = diameter_codegen:from_dict(FileName, Spec, Opts, hrl),
HrlFile = filename:join("src", FileName ++ ".hrl"),
+ ErlFile = filename:join("src", FileName ++ ".erl"),
+ ErlCOpts = [{outdir, "ebin"}] ++
+ rebar_config:get(Config, erl_opts, []),
+ _ = compile:file(ErlFile, ErlCOpts),
case filelib:is_regular(HrlFile) of
true ->
ok = rebar_file_utils:mv(HrlFile, "include");
@@ -86,15 +106,26 @@ compile_dia(Source, Target, Config) ->
ok
end;
{error, Reason} ->
- ?ERROR("~s~n", [diameter_dict_util:format_error(Reason)])
+ ?ABORT(
+ "Compiling ~s failed: ~s~n",
+ [Source, diameter_dict_util:format_error(Reason)]
+ )
end.
dia_generated_files(DiaDir, SrcDir, IncDir) ->
F = fun(File, Acc) ->
- {ok, Spec} = diameter_dict_util:parse({path, File}, []),
- FileName = dia_filename(File, Spec),
- [filename:join([IncDir, FileName ++ ".hrl"]) |
- filelib:wildcard(filename:join([SrcDir, FileName ++ ".*"]))] ++ Acc
+ case catch diameter_dict_util:parse({path, File}, []) of
+ {ok, Spec} ->
+ FileName = dia_filename(File, Spec),
+ [
+ filename:join([IncDir, FileName ++ ".hrl"]) |
+ filelib:wildcard(
+ filename:join([SrcDir, FileName ++ ".*"])
+ )
+ ] ++ Acc;
+ _ ->
+ Acc
+ end
end,
lists:foldl(F, [], filelib:wildcard(filename:join([DiaDir, "*.dia"]))).
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index 56f1ebd..937fe5f 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -36,25 +36,17 @@
-include("rebar.hrl").
-include_lib("stdlib/include/erl_compile.hrl").
--define(ERLCINFO_VSN, 1).
+-define(ERLCINFO_VSN, 2).
-define(ERLCINFO_FILE, "erlcinfo").
-type erlc_info_v() :: {digraph:vertex(), term()} | 'false'.
-type erlc_info_e() :: {digraph:vertex(), digraph:vertex()}.
--type erlc_info() :: {list(erlc_info_v()), list(erlc_info_e())}.
+-type erlc_info() :: {list(erlc_info_v()), list(erlc_info_e()), list(string())}.
-record(erlcinfo,
{
vsn = ?ERLCINFO_VSN :: pos_integer(),
- info = {[], []} :: erlc_info()
+ info = {[], [], []} :: erlc_info()
}).
--ifdef(namespaced_types).
-%% digraph:graph() exists starting from Erlang 17.
--type rebar_digraph() :: digraph:graph().
--else.
-%% digraph() has been obsoleted in Erlang 17 and deprecated in 18.
--type rebar_digraph() :: digraph().
--endif.
-
%% ===================================================================
%% Public API
%% ===================================================================
@@ -109,7 +101,7 @@ compile(Config, _AppFile) ->
doterl_compile(Config, "ebin").
-spec clean(rebar_config:config(), file:filename()) -> 'ok'.
-clean(Config, _AppFile) ->
+clean(_Config, _AppFile) ->
MibFiles = rebar_utils:find_files_by_ext("mibs", ".mib"),
MIBs = [filename:rootname(filename:basename(MIB)) || MIB <- MibFiles],
rebar_file_utils:delete_each(
@@ -123,7 +115,7 @@ clean(Config, _AppFile) ->
|| F <- YrlFiles ]),
%% Delete the build graph, if any
- rebar_file_utils:rm_rf(erlcinfo_file(Config)),
+ rebar_file_utils:rm_rf(erlcinfo_file()),
%% Erlang compilation is recursive, so it's possible that we have a nested
%% directory structure in ebin with .beam files within. As such, we want
@@ -292,144 +284,125 @@ doterl_compile(Config, OutDir) ->
doterl_compile(Config, OutDir, [], ErlOpts).
doterl_compile(Config, OutDir, MoreSources, ErlOpts) ->
- ErlFirstFilesConf = rebar_config:get_list(Config, erl_first_files, []),
?DEBUG("erl_opts ~p~n", [ErlOpts]),
%% Support the src_dirs option allowing multiple directories to
%% contain erlang source. This might be used, for example, should
%% eunit tests be separated from the core application source.
SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources,
- %% NOTE: If and when erl_first_files is not inherited anymore
- %% (rebar_config:get_local instead of rebar_config:get_list), consider
- %% logging a warning message for any file listed in erl_first_files which
- %% wasn't found via gather_src.
- RestErls = [File || File <- AllErlFiles,
- not lists:member(File, ErlFirstFilesConf)],
- %% NOTE: order of files in ErlFirstFiles is important!
- ErlFirstFiles = [File || File <- ErlFirstFilesConf,
- lists:member(File, AllErlFiles)],
+
%% Make sure that ebin/ exists and is on the path
ok = filelib:ensure_dir(filename:join("ebin", "dummy.beam")),
CurrPath = code:get_path(),
true = code:add_path(filename:absname("ebin")),
OutDir1 = proplists:get_value(outdir, ErlOpts, OutDir),
- G = init_erlcinfo(Config, AllErlFiles),
- %% Split RestErls so that files which are depended on are treated
- %% like erl_first_files.
- {OtherFirstErls, OtherErls} =
+
+ G = init_erlcinfo(proplists:get_all_values(i, ErlOpts), AllErlFiles),
+ NeededErlFiles = needed_files(G, OutDir1, AllErlFiles),
+ {ErlFirstFiles, ErlOptsFirst} = erl_first_files(Config,
+ ErlOpts,
+ NeededErlFiles),
+ {DepErls, OtherErls} =
lists:partition(
- fun(F) ->
- Children = get_children(G, F),
- log_files(?FMT("Files dependent on ~s", [F]), Children),
-
- case erls(Children) of
- [] ->
- %% There are no files dependent on this file.
- false;
- _ ->
- %% There are some files dependent on the file.
- %% Thus the file has higher priority
- %% and should be compiled in the first place.
- true
- end
- end, RestErls),
- %% Dependencies of OtherFirstErls that must be compiled first.
- OtherFirstErlsDeps = lists:flatmap(
- fun(Erl) -> erls(get_parents(G, Erl)) end,
- OtherFirstErls),
- %% NOTE: In case the way we retrieve OtherFirstErlsDeps or merge
- %% it with OtherFirstErls does not result in the correct compile
- %% priorities, or the method in use proves to be too slow for
- %% certain projects, consider using a more elaborate method (maybe
- %% digraph_utils) or alternatively getting and compiling the .erl
- %% parents of an individual Source in internal_erl_compile. By not
- %% handling this in internal_erl_compile, we also avoid extra
- %% needs_compile/2 calls.
- FirstErls = ErlFirstFiles ++ uo_merge(OtherFirstErlsDeps, OtherFirstErls),
+ fun(Source) ->
+ digraph:in_degree(G, Source) > 0
+ end,
+ [F || F <- NeededErlFiles, not lists:member(F, ErlFirstFiles)]),
+ DepErlsOrdered = digraph_utils:topsort(digraph_utils:subgraph(G, DepErls)),
+ FirstErls = ErlFirstFiles ++ lists:reverse(DepErlsOrdered),
?DEBUG("Files to compile first: ~p~n", [FirstErls]),
+
rebar_base_compiler:run(
Config, FirstErls, OtherErls,
fun(S, C) ->
- internal_erl_compile(C, S, OutDir1, ErlOpts, G)
+ ErlOpts1 = case lists:member(S, ErlFirstFiles) of
+ true -> ErlOptsFirst;
+ false -> ErlOpts
+ end,
+ internal_erl_compile(C, S, OutDir1, ErlOpts1)
end),
true = rebar_utils:cleanup_code_path(CurrPath),
ok.
-%%
-%% Return all .erl files from a list of files
-%%
-erls(Files) ->
- [Erl || Erl <- Files, filename:extension(Erl) =:= ".erl"].
-
-%%
-%% Return a list without duplicates while preserving order
-%%
-ulist(L) ->
- ulist(L, []).
-
-ulist([H|T], Acc) ->
- case lists:member(H, T) of
- true ->
- ulist(T, Acc);
- false ->
- ulist(T, [H|Acc])
- end;
-ulist([], Acc) ->
- lists:reverse(Acc).
-
-%%
-%% Merge two lists without duplicates while preserving order
-%%
-uo_merge(L1, L2) ->
- lists:foldl(fun(E, Acc) -> u_add_element(E, Acc) end, ulist(L1), L2).
-
-u_add_element(Elem, [Elem|_]=Set) -> Set;
-u_add_element(Elem, [E1|Set]) -> [E1|u_add_element(Elem, Set)];
-u_add_element(Elem, []) -> [Elem].
-
--spec include_path(file:filename(),
- rebar_config:config()) -> [file:filename(), ...].
-include_path(Source, Config) ->
- ErlOpts = rebar_config:get(Config, erl_opts, []),
- lists:usort(["include", filename:dirname(Source)]
- ++ proplists:get_all_values(i, ErlOpts)).
-
--spec needs_compile(file:filename(), file:filename(),
- [string()]) -> boolean().
-needs_compile(Source, Target, Parents) ->
- TargetLastMod = filelib:last_modified(Target),
- lists:any(fun(I) -> TargetLastMod < filelib:last_modified(I) end,
- [Source] ++ Parents).
-
-check_erlcinfo(_Config, #erlcinfo{vsn=?ERLCINFO_VSN}) ->
- ok;
-check_erlcinfo(Config, #erlcinfo{vsn=Vsn}) ->
- ?ABORT("~s file version is incompatible. expected: ~b got: ~b~n",
- [erlcinfo_file(Config), ?ERLCINFO_VSN, Vsn]);
-check_erlcinfo(Config, _) ->
- ?ABORT("~s file is invalid. Please delete before next run.~n",
- [erlcinfo_file(Config)]).
-
-erlcinfo_file(_Config) ->
+%% Get files which need to be compiled first, i.e. those specified in
+%% erl_first_files and parse_transform options. Also produce specific
+%% erl_opts for these first files, so that yet to be compiled parse
+%% transformations are excluded from it.
+erl_first_files(Config, ErlOpts, NeededErlFiles) ->
+ %% NOTE: rebar_config:get_local perhaps?
+ ErlFirstFilesConf = rebar_config:get_list(Config, erl_first_files, []),
+ NeededSrcDirs = lists:usort(
+ lists:map(fun filename:dirname/1, NeededErlFiles)),
+ %% NOTE: order of files here is important!
+ ErlFirstFiles = lists:filter(
+ fun(File) -> lists:member(File, NeededErlFiles) end,
+ ErlFirstFilesConf),
+ {ParseTransforms, ParseTransformsErls} =
+ lists:unzip(
+ lists:flatmap(
+ fun(PT) ->
+ PTerls = [filename:join(Dir, module_to_erl(PT))
+ || Dir <- NeededSrcDirs],
+ [{PT, PTerl} || PTerl <- PTerls,
+ lists:member(PTerl, NeededErlFiles)]
+ end,
+ proplists:get_all_values(parse_transform, ErlOpts))),
+ ErlOptsFirst = lists:filter(
+ fun ({parse_transform, PT}) ->
+ not lists:member(PT, ParseTransforms);
+ (_) -> true
+ end,
+ ErlOpts),
+ {ErlFirstFiles ++ ParseTransformsErls, ErlOptsFirst}.
+
+%% Get subset of SourceFiles which need to be recompiled, respecting
+%% dependencies induced by given graph G.
+needed_files(G, OutDir, SourceFiles) ->
+ lists:filter(
+ fun(Source) ->
+ Target = target_base(OutDir, Source) ++ ".beam",
+ digraph:vertex(G, Source) >
+ {Source, filelib:last_modified(Target)}
+ end, SourceFiles).
+
+target_base(OutDir, Source) ->
+ filename:join(OutDir, filename:basename(Source, ".erl")).
+
+erlcinfo_file() ->
filename:join([rebar_utils:get_cwd(), ".rebar", ?ERLCINFO_FILE]).
-init_erlcinfo(Config, Erls) ->
- G = restore_erlcinfo(Config),
- %% Get a unique list of dirs based on the source files' locations.
- %% This is used for finding files in sub dirs of the configured
- %% src_dirs. For example, src/sub_dir/foo.erl.
- Dirs = sets:to_list(lists:foldl(
- fun(Erl, Acc) ->
- Dir = filename:dirname(Erl),
- sets:add_element(Dir, Acc)
- end, sets:new(), Erls)),
- Updates = [update_erlcinfo(G, Erl, include_path(Erl, Config) ++ Dirs)
- || Erl <- Erls],
- Modified = lists:member(modified, Updates),
- ok = store_erlcinfo(G, Config, Modified),
+%% Get dependency graph of given Erls files and their dependencies
+%% (header files, parse transforms, behaviours etc.) located in their
+%% directories or given InclDirs. Note that last modification times
+%% stored in vertices already respect
+%% dependencies induced by given graph G.
+init_erlcinfo(InclDirs, Erls) ->
+ G = digraph:new([acyclic]),
+ try restore_erlcinfo(G, InclDirs)
+ catch
+ _:_ ->
+ ?WARN("Failed to restore ~s file. Discarding it.~n",
+ [erlcinfo_file()]),
+ ok = file:delete(erlcinfo_file())
+ end,
+ Dirs = source_and_include_dirs(InclDirs, Erls),
+ Modified = lists:foldl(update_erlcinfo_fun(G, Dirs), false, Erls),
+ if Modified -> store_erlcinfo(G, InclDirs); not Modified -> ok end,
G.
-update_erlcinfo(G, Source, Dirs) ->
+source_and_include_dirs(InclDirs, Erls) ->
+ SourceDirs = lists:map(fun filename:dirname/1, Erls),
+ lists:usort(["include" | InclDirs ++ SourceDirs]).
+
+update_erlcinfo_fun(G, Dirs) ->
+ fun(Erl, Modified) ->
+ case update_erlcinfo(G, Dirs, Erl) of
+ modified -> true;
+ unmodified -> Modified
+ end
+ end.
+
+update_erlcinfo(G, Dirs, Source) ->
case digraph:vertex(G, Source) of
{_, LastUpdated} ->
case filelib:last_modified(Source) of
@@ -440,78 +413,74 @@ update_erlcinfo(G, Source, Dirs) ->
digraph:del_vertex(G, Source),
modified;
LastModified when LastUpdated < LastModified ->
- modify_erlcinfo(G, Source, Dirs),
- modified;
+ modify_erlcinfo(G, Source, LastModified, Dirs);
_ ->
- unmodified
+ Modified = lists:foldl(
+ update_erlcinfo_fun(G, Dirs),
+ false, digraph:out_neighbours(G, Source)),
+ MaxModified = update_max_modified_deps(G, Source),
+ case Modified orelse MaxModified > LastUpdated of
+ true -> modified;
+ false -> unmodified
+ end
end;
false ->
- modify_erlcinfo(G, Source, Dirs),
- modified
+ modify_erlcinfo(G, Source, filelib:last_modified(Source), Dirs)
end.
-modify_erlcinfo(G, Source, Dirs) ->
+update_max_modified_deps(G, Source) ->
+ MaxModified = lists:max(
+ lists:map(
+ fun(File) ->
+ {_, MaxModified} = digraph:vertex(G, File),
+ MaxModified
+ end,
+ [Source|digraph:out_neighbours(G, Source)])),
+ digraph:add_vertex(G, Source, MaxModified),
+ MaxModified.
+
+modify_erlcinfo(G, Source, LastModified, Dirs) ->
{ok, Fd} = file:open(Source, [read]),
Incls = parse_attrs(Fd, []),
AbsIncls = expand_file_names(Incls, Dirs),
ok = file:close(Fd),
- LastUpdated = {date(), time()},
- digraph:add_vertex(G, Source, LastUpdated),
+ digraph:add_vertex(G, Source, LastModified),
+ digraph:del_edges(G, digraph:out_edges(G, Source)),
lists:foreach(
fun(Incl) ->
- update_erlcinfo(G, Incl, Dirs),
+ update_erlcinfo(G, Dirs, Incl),
digraph:add_edge(G, Source, Incl)
- end, AbsIncls).
+ end, AbsIncls),
+ modified.
-restore_erlcinfo(Config) ->
- File = erlcinfo_file(Config),
- G = digraph:new(),
- case file:read_file(File) of
+restore_erlcinfo(G, InclDirs) ->
+ case file:read_file(erlcinfo_file()) of
{ok, Data} ->
- try binary_to_term(Data) of
- Erlcinfo ->
- ok = check_erlcinfo(Config, Erlcinfo),
- #erlcinfo{info=ErlcInfo} = Erlcinfo,
- {Vs, Es} = ErlcInfo,
- lists:foreach(
- fun({V, LastUpdated}) ->
- digraph:add_vertex(G, V, LastUpdated)
- end, Vs),
- lists:foreach(
- fun({V1, V2}) ->
- digraph:add_edge(G, V1, V2)
- end, Es)
- catch
- error:badarg ->
- ?ERROR(
- "Failed (binary_to_term) to restore rebar info file."
- " Discard file.~n", []),
- ok
- end;
- _Err ->
+ %% Since externally passed InclDirs can influence erlcinfo
+ %% graph (see modify_erlcinfo), we have to check here that
+ %% they didn't change.
+ #erlcinfo{vsn=?ERLCINFO_VSN, info={Vs, Es, InclDirs}} =
+ binary_to_term(Data),
+ lists:foreach(
+ fun({V, LastUpdated}) ->
+ digraph:add_vertex(G, V, LastUpdated)
+ end, Vs),
+ lists:foreach(
+ fun({_, V1, V2, _}) ->
+ digraph:add_edge(G, V1, V2)
+ end, Es);
+ {error, _} ->
ok
- end,
- G.
+ end.
-store_erlcinfo(_G, _Config, _Modified = false) ->
- ok;
-store_erlcinfo(G, Config, _Modified) ->
- Vs = lists:map(
- fun(V) ->
- digraph:vertex(G, V)
- end, digraph:vertices(G)),
- Es = lists:flatmap(
- fun({V, _}) ->
- lists:map(
- fun(E) ->
- {_, V1, V2, _} = digraph:edge(G, E),
- {V1, V2}
- end, digraph:out_edges(G, V))
- end, Vs),
- File = erlcinfo_file(Config),
+store_erlcinfo(G, InclDirs) ->
+ Vs = lists:map(fun(V) -> digraph:vertex(G, V) end, digraph:vertices(G)),
+ Es = lists:map(fun(E) -> digraph:edge(G, E) end, digraph:edges(G)),
+ File = erlcinfo_file(),
ok = filelib:ensure_dir(File),
- Data = term_to_binary(#erlcinfo{info={Vs, Es}}, [{compressed, 9}]),
- file:write_file(File, Data).
+ Info = #erlcinfo{info={Vs, Es, InclDirs}},
+ Data = term_to_binary(Info, [{compressed, 2}]),
+ ok = file:write_file(File, Data).
%% NOTE: If, for example, one of the entries in Files refers to
%% gen_server.erl, that entry will be dropped. It is dropped because
@@ -545,46 +514,21 @@ expand_file_names(Files, Dirs) ->
end
end, Files).
--spec get_parents(rebar_digraph(), file:filename()) -> [file:filename()].
-get_parents(G, Source) ->
- %% Return all files which the Source depends upon.
- digraph_utils:reachable_neighbours([Source], G).
-
--spec get_children(rebar_digraph(), file:filename()) -> [file:filename()].
-get_children(G, Source) ->
- %% Return all files dependent on the Source.
- digraph_utils:reaching_neighbours([Source], G).
-
--spec internal_erl_compile(rebar_config:config(), file:filename(),
- file:filename(), list(),
- rebar_digraph()) -> 'ok' | 'skipped'.
-internal_erl_compile(Config, Source, OutDir, ErlOpts, G) ->
- %% Determine the target name and includes list by inspecting the source file
- Module = filename:basename(Source, ".erl"),
- Parents = get_parents(G, Source),
- log_files(?FMT("Dependencies of ~s", [Source]), Parents),
-
- %% Construct the target filename
- Target = filename:join([OutDir | string:tokens(Module, ".")]) ++ ".beam",
- ok = filelib:ensure_dir(Target),
-
- %% If the file needs compilation, based on last mod date of includes or
- %% the target
- case needs_compile(Source, Target, Parents) of
- true ->
- Opts = [{outdir, filename:dirname(Target)}] ++
- ErlOpts ++ [{i, "include"}, return],
- case compile:file(Source, Opts) of
- {ok, _Mod} ->
- ok;
- {ok, _Mod, Ws} ->
- rebar_base_compiler:ok_tuple(Config, Source, Ws);
- {error, Es, Ws} ->
- rebar_base_compiler:error_tuple(Config, Source,
- Es, Ws, Opts)
- end;
- false ->
- skipped
+-spec internal_erl_compile(
+ rebar_config:config(),
+ file:filename(),
+ file:filename(),
+ list()) -> ok | {ok, any()} | {error, any(), any()}.
+internal_erl_compile(Config, Source, OutDir, ErlOpts) ->
+ ok = filelib:ensure_dir(OutDir),
+ Opts = [{outdir, OutDir}] ++ ErlOpts ++ [{i, "include"}, return],
+ case compile:file(Source, Opts) of
+ {ok, _Mod} ->
+ ok;
+ {ok, _Mod, Ws} ->
+ rebar_base_compiler:ok_tuple(Config, Source, Ws);
+ {error, Es, Ws} ->
+ rebar_base_compiler:error_tuple(Config, Source, Es, Ws, Opts)
end.
-spec compile_mib(file:filename(), file:filename(),
@@ -627,7 +571,7 @@ compile_yrl(Source, Target, Config) ->
-spec compile_xrl_yrl(rebar_config:config(), file:filename(),
file:filename(), list(), module()) -> 'ok'.
compile_xrl_yrl(Config, Source, Target, Opts, Mod) ->
- case needs_compile(Source, Target, []) of
+ case needs_compile(Source, Target) of
true ->
case Mod:file(Source, Opts ++ [{return, true}]) of
{ok, _} ->
@@ -642,6 +586,9 @@ compile_xrl_yrl(Config, Source, Target, Opts, Mod) ->
skipped
end.
+needs_compile(Source, Target) ->
+ filelib:last_modified(Source) > filelib:last_modified(Target).
+
gather_src([], Srcs) ->
Srcs;
gather_src([Dir|Rest], Srcs) ->
@@ -676,21 +623,15 @@ parse_attrs(Fd, Includes) ->
end.
process_attr(Form, Includes) ->
- try
- AttrName = erl_syntax:atom_value(erl_syntax:attribute_name(Form)),
- process_attr(AttrName, Form, Includes)
- catch _:_ ->
- %% TODO: We should probably try to be more specific here
- %% and not suppress all errors.
- Includes
- end.
+ AttrName = erl_syntax:atom_value(erl_syntax:attribute_name(Form)),
+ process_attr(AttrName, Form, Includes).
process_attr(import, Form, Includes) ->
case erl_syntax_lib:analyze_import_attribute(Form) of
{Mod, _Funs} ->
- [atom_to_list(Mod) ++ ".erl"|Includes];
+ [module_to_erl(Mod)|Includes];
Mod ->
- [atom_to_list(Mod) ++ ".erl"|Includes]
+ [module_to_erl(Mod)|Includes]
end;
process_attr(file, Form, Includes) ->
{File, _} = erl_syntax_lib:analyze_file_attribute(Form),
@@ -702,29 +643,35 @@ process_attr(include, Form, Includes) ->
process_attr(include_lib, Form, Includes) ->
[FileNode] = erl_syntax:attribute_arguments(Form),
RawFile = erl_syntax:string_value(FileNode),
- File = maybe_expand_include_lib_path(RawFile),
- [File|Includes];
+ maybe_expand_include_lib_path(RawFile) ++ Includes;
process_attr(behaviour, Form, Includes) ->
[FileNode] = erl_syntax:attribute_arguments(Form),
- File = erl_syntax:atom_name(FileNode) ++ ".erl",
+ File = module_to_erl(erl_syntax:atom_value(FileNode)),
[File|Includes];
process_attr(compile, Form, Includes) ->
[Arg] = erl_syntax:attribute_arguments(Form),
case erl_syntax:concrete(Arg) of
{parse_transform, Mod} ->
- [atom_to_list(Mod) ++ ".erl"|Includes];
+ [module_to_erl(Mod)|Includes];
{core_transform, Mod} ->
- [atom_to_list(Mod) ++ ".erl"|Includes];
+ [module_to_erl(Mod)|Includes];
L when is_list(L) ->
lists:foldl(
- fun({parse_transform, M}, Acc) ->
- [atom_to_list(M) ++ ".erl"|Acc];
- ({core_transform, M}, Acc) ->
- [atom_to_list(M) ++ ".erl"|Acc];
+ fun({parse_transform, Mod}, Acc) ->
+ [module_to_erl(Mod)|Acc];
+ ({core_transform, Mod}, Acc) ->
+ [module_to_erl(Mod)|Acc];
(_, Acc) ->
Acc
- end, Includes, L)
- end.
+ end, Includes, L);
+ _ ->
+ Includes
+ end;
+process_attr(_, _Form, Includes) ->
+ Includes.
+
+module_to_erl(Mod) ->
+ atom_to_list(Mod) ++ ".erl".
%% Given the filename from an include_lib attribute, if the path
%% exists, return unmodified, or else get the absolute ERL_LIBS
@@ -732,7 +679,7 @@ process_attr(compile, Form, Includes) ->
maybe_expand_include_lib_path(File) ->
case filelib:is_regular(File) of
true ->
- File;
+ [File];
false ->
expand_include_lib_path(File)
end.
@@ -744,11 +691,17 @@ maybe_expand_include_lib_path(File) ->
%% utilize more elaborate logic.
expand_include_lib_path(File) ->
File1 = filename:basename(File),
- Split = filename:split(filename:dirname(File)),
- Lib = hd(Split),
- SubDir = filename:join(tl(Split)),
- Dir = code:lib_dir(list_to_atom(Lib), list_to_atom(SubDir)),
- filename:join(Dir, File1).
+ [Lib | Parts] = filename:split(filename:dirname(File)),
+ SubDir = case Parts of
+ [] -> % prevent function clause error
+ [];
+ _ ->
+ filename:join(Parts)
+ end,
+ case code:lib_dir(list_to_atom(Lib), list_to_atom(SubDir)) of
+ {error, bad_name} -> [];
+ Dir -> [filename:join(Dir, File1)]
+ end.
%%
%% Ensure all files in a list are present and abort if one is missing
@@ -762,13 +715,3 @@ check_file(File) ->
false -> ?ABORT("File ~p is missing, aborting\n", [File]);
true -> File
end.
-
-%% Print prefix followed by list of files. If the list is empty, print
-%% on the same line, otherwise use a separate line.
-log_files(Prefix, Files) ->
- case Files of
- [] ->
- ?DEBUG("~s: ~p~n", [Prefix, Files]);
- _ ->
- ?DEBUG("~s:~n~p~n", [Prefix, Files])
- end.
diff --git a/src/rebar_xref.erl b/src/rebar_xref.erl
index 444134d..5eb4499 100644
--- a/src/rebar_xref.erl
+++ b/src/rebar_xref.erl
@@ -187,7 +187,8 @@ keyall(Key, List) ->
lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List).
get_behaviour_callbacks(exports_not_used, Attributes) ->
- [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes)];
+ [B:behaviour_info(callbacks) ||
+ B <- keyall(behaviour, Attributes) ++ keyall(behavior, Attributes)];
get_behaviour_callbacks(_XrefCheck, _Attributes) ->
[].