From 4ff52d991eb1240acbe82859d643295076f90727 Mon Sep 17 00:00:00 2001 From: Louis-Philippe Gauthier Date: Wed, 15 Jun 2016 08:05:21 -0400 Subject: Add support for cover_excl_mods --- rebar.config.sample | 3 +++ src/rebar_prv_cover.erl | 38 +++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/rebar.config.sample b/rebar.config.sample index f57f8dc..cc027f9 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -57,6 +57,9 @@ %% is `false' {cover_enabled, false}. +%% Modules to exclude from cover +{cover_excl_mods, []}. + %% Options to pass to cover provider {cover_opts, [verbose]}. diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index e555a9b..b04b1c1 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -302,18 +302,30 @@ cover_compile(State, Dirs) -> {ok, CoverPid} = start_cover(), %% redirect cover output true = redirect_cover_output(State, CoverPid), + ExclMods = rebar_state:get(State, cover_excl_mods, []), + lists:foreach(fun(Dir) -> - ?DEBUG("cover compiling ~p", [Dir]), - case catch(cover:compile_beam_directory(Dir)) of - {error, eacces} -> - ?WARN("Directory ~p not readable, modules will not be included in coverage", [Dir]); - {error, enoent} -> - ?WARN("Directory ~p not found", [Dir]); - {'EXIT', {Reason, _}} -> - ?WARN("Cover compilation for directory ~p failed: ~p", [Dir, Reason]); - Results -> - %% print any warnings about modules that failed to cover compile - lists:foreach(fun print_cover_warnings/1, lists:flatten(Results)) + case file:list_dir(Dir) of + {ok, Files} -> + Files2 = [F || F <- Files, filename:extension(F) == ".beam"], + lists:foreach(fun(File) -> + case lists:any(fun (Excl) -> + File =:= (atom_to_list(Excl) ++ ".beam") + end, ExclMods) of + true -> + ?DEBUG("cover ignoring ~p ~p", [Dir, File]); + _ -> + ?DEBUG("cover compiling ~p ~p", [Dir, File]), + case catch(cover:compile_beam(filename:join(Dir, File))) of + {error, Reason} -> + ?WARN("Cover compilation failed: ~p", [Reason]); + {ok, _} -> + ok + end + end + end, Files2); + {error, Reason} -> + ?WARN("Directory ~p error ~p", [Dir, Reason]) end end, Dirs). @@ -346,10 +358,6 @@ redirect_cover_output(State, CoverPid) -> [append]), group_leader(F, CoverPid). -print_cover_warnings({ok, _}) -> ok; -print_cover_warnings({error, Error}) -> - ?WARN("Cover compilation failed: ~p", [Error]). - write_coverdata(State, Task) -> DataDir = cover_dir(State), ok = filelib:ensure_dir(filename:join([DataDir, "dummy.log"])), -- cgit v1.1 From 48565557e4cf0c82e54352bef088633bb34876db Mon Sep 17 00:00:00 2001 From: Stuart Thackray Date: Tue, 12 Jul 2016 10:36:47 +0200 Subject: upgrade relx version --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 0489572..f033694 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.0.4"}, - {relx, "3.19.0"}, + {relx, "3.20.0"}, {cf, "0.2.1"}, {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. -- cgit v1.1 From b911b758123f781bcf6951a61bad44e818a17023 Mon Sep 17 00:00:00 2001 From: Stuart Thackray Date: Tue, 12 Jul 2016 14:09:04 +0200 Subject: rebar update relx --- rebar.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.lock b/rebar.lock index 66241a9..d5a61d7 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,7 +7,7 @@ {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.20.0">>},0}, {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]}. [ {pkg_hash,[ @@ -19,6 +19,6 @@ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, + {<<"relx">>, <<"B515B8317D25B3A1508699294C3D1FA6DC0527851DFFC87446661BCE21A36710">>}, {<<"ssl_verify_hostname">>, <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]} ]. -- cgit v1.1 From 0b3a8ad77e1fcdd05ae9d568abee191f6be31975 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 12 Jul 2016 08:23:07 -0400 Subject: Add issue template to help people fill tickets --- .github/ISSUE_TEMPLATE.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..6d5a0c2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,31 @@ +### Pre-Check ### + +- If you are filing for a bug, please do a quick search in current issues first +- For bugs, mention if you are willing or interested in helping fix the issue +- For questions or support, it helps to include context around your project or problem +- Think of a descriptive title (more descriptive than 'feature X is broken' unless it is fully broken) + +### Environment ### + +- Add the result of `rebar3 report` to your message: + +``` +$ rebar3 report "my failing command" +... +``` + +- Verify whether the version of rebar3 you're running is the latest release (see https://github.com/erlang/rebar3/releases) +- If possible, include information about your project and its structure. Open source projects or examples are always easier to debug. + If you can provide an example code base to reproduce the issue on, we will generally be able to provide more help, and faster. + +### Current behaviour ### + +Describe the current behaviour. In case of a failure, crash, or exception, please include the result of running the command with debug information: + +``` +DEBUG=1 rebar3 +``` + +### Expected behaviour ### + +Describe what you expected to happen. -- cgit v1.1 From b75f95e063f493ff19dca9aef54ce42b006e9611 Mon Sep 17 00:00:00 2001 From: Ilya Khaprov Date: Fri, 15 Jul 2016 14:12:30 +0300 Subject: ssl_verify_hostname was renamed to ssl_verify_fun --- rebar.config | 4 +-- rebar.lock | 4 +-- src/rebar.app.src | 2 +- test/rebar_unlock_SUITE_data/pkg.rebar.lock | 56 +++++++++++++---------------- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/rebar.config b/rebar.config index f033694..f91d61b 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ %% ex: ts=4 sw=4 ft=erlang et {deps, [{erlware_commons, "0.21.0"}, - {ssl_verify_hostname, "1.0.5"}, + {ssl_verify_fun, "1.1.1"}, {certifi, "0.4.0"}, {providers, "1.6.0"}, {getopt, "0.8.2"}, @@ -37,7 +37,7 @@ {bootstrap, []}, {dialyze, [{overrides, [{add, erlware_commons, [{erl_opts, [debug_info]}]}, - {add, ssl_verify_hostname, [{erl_opts, [debug_info]}]}, + {add, ssl_verify_fun, [{erl_opts, [debug_info]}]}, {add, certifi, [{erl_opts, [debug_info]}]}, {add, providers, [{erl_opts, [debug_info]}]}, {add, getopt, [{erl_opts, [debug_info]}]}, diff --git a/rebar.lock b/rebar.lock index d5a61d7..231e266 100644 --- a/rebar.lock +++ b/rebar.lock @@ -8,7 +8,7 @@ {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, {<<"relx">>,{pkg,<<"relx">>,<<"3.20.0">>},0}, - {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]}. + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, @@ -20,5 +20,5 @@ {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, {<<"relx">>, <<"B515B8317D25B3A1508699294C3D1FA6DC0527851DFFC87446661BCE21A36710">>}, - {<<"ssl_verify_hostname">>, <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]} + {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} ]. diff --git a/src/rebar.app.src b/src/rebar.app.src index bd0f871..5b735cf 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -23,7 +23,7 @@ erlware_commons, providers, bbmustache, - ssl_verify_hostname, + ssl_verify_fun, certifi, cth_readable, relx, diff --git a/test/rebar_unlock_SUITE_data/pkg.rebar.lock b/test/rebar_unlock_SUITE_data/pkg.rebar.lock index 38e22e5..231e266 100644 --- a/test/rebar_unlock_SUITE_data/pkg.rebar.lock +++ b/test/rebar_unlock_SUITE_data/pkg.rebar.lock @@ -1,32 +1,24 @@ -{"1.1.0",[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, - {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, - {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, - {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, - {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, - {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0}, - {<<"ssl_verify_hostname">>, - {pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>}, - 0}]}. -[{pkg_hash,[{<<"bbmustache">>, - <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, - {<<"certifi">>, - <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, - {<<"cf">>, - <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, - {<<"cth_readable">>, - <<"983913A8E8572310B7EAF5F2631148B7D70B3C090D2120DCFE777A93AA4165FB">>}, - {<<"erlware_commons">>, - <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, - {<<"eunit_formatters">>, - <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, - {<<"getopt">>, - <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, - {<<"providers">>, - <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, - <<"286DD5244B4786F56AAC75D5C8E2D1FB4CFD306810D4EC8548F3AE1B3AADB8F7">>}, - {<<"ssl_verify_hostname">>, - <<"2E73E068CD6393526F9FA6D399353D7C9477D6886BA005F323B592D389FB47BE">>}]}]. +{"1.1.0", +[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, + {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, + {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.20.0">>},0}, + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. +[ +{pkg_hash,[ + {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, + {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, + {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, + {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, + {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, + {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, + {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, + {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, + {<<"relx">>, <<"B515B8317D25B3A1508699294C3D1FA6DC0527851DFFC87446661BCE21A36710">>}, + {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} +]. -- cgit v1.1 From ffe5e42521aef4315be97f7baff2c7819fd79237 Mon Sep 17 00:00:00 2001 From: Guillaume Bour Date: Fri, 22 Jul 2016 10:43:16 +0200 Subject: Fix support for `not_valid` dialyzer error --- src/rebar_prv_dialyzer.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 82d2d07..0376918 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -260,6 +260,9 @@ read_plt(_State, Plt) -> Result; {error, no_such_file} -> error; + {error, not_valid} -> + Error = io_lib:format("Could not read the PLT file ~p", [Plt]), + throw({dialyzer_error, Error}); {error, read_error} -> Error = io_lib:format("Could not read the PLT file ~p", [Plt]), throw({dialyzer_error, Error}) -- cgit v1.1 From 2c7296babf5849dba383b52894490e397acb64d2 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 22 Jul 2016 08:12:46 -0400 Subject: Fix opts check when compiler called with dict opts rebar_base_compiler allows to be called with two types of options: a dictionary, or a rebar_state record. In the later case, the options are taken out with a call from rebar_opts, which fetches options that have been inserted in the application via rebar_app_info as part of the app_discovery phase, and are a list. This yields a possibility that options used when formatting warnings can either be a list of a dict, and we only used lists when making checks. This ended up breaking 3rd party compiler users (i.e. LFE compile plugin) since they were calling us with a dict rather than our own internal records. This patch supports both types of lookups to avoid issues. --- src/rebar_base_compiler.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 6b8c7ca..5d54057 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -155,7 +155,13 @@ format_warnings(Source, Warnings) -> format_warnings(Source, Warnings, []). format_warnings(Source, Warnings, Opts) -> - Prefix = case lists:member(warnings_as_errors, Opts) of + %% `Opts' can be passed in both as a list or a dictionary depending + %% on whether the first call to rebar_erlc_compiler was done with + %% the type `rebar_dict()' or `rebar_state:t()'. + LookupFn = if is_list(Opts) -> fun lists:member/2 + ; true -> fun dict:is_key/2 + end, + Prefix = case LookupFn(warnings_as_errors, Opts) of true -> ""; false -> "Warning: " end, -- cgit v1.1 From c8ec1546e5aa0a5b5aafad7d45d754b6464c2d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Serre?= Date: Sat, 23 Jul 2016 19:45:46 +0200 Subject: template_dir option was forgotten in documentation plus recurse in directory added --- rebar.config.sample | 3 ++- src/rebar_templater.erl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rebar.config.sample b/rebar.config.sample index f57f8dc..54f32b2 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -128,7 +128,8 @@ %% Paths to miscellaneous Erlang files to compile for an app %% without including them in its modules list {extra_src_dirs, []}. - +%% Path where custom rebar3 templates could be found +{template_dir, []}. %% == EDoc == diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 2f33bfc..299b957 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -326,7 +326,7 @@ find_other_templates(State) -> undefined -> []; TemplateDir -> - rebar_utils:find_files(TemplateDir, ?TEMPLATE_RE) + rebar_utils:find_files(TemplateDir, ?TEMPLATE_RE, true) % recursive end. %% Fetch template indexes that sit on disk in plugins -- cgit v1.1 From b668329a9e7929f07f00d9a0647714690da4b4bd Mon Sep 17 00:00:00 2001 From: Guillaume Bour Date: Sat, 30 Jul 2016 13:42:00 +0200 Subject: Fix return error on `not_valid` to force .plt file regeneration --- src/rebar_prv_dialyzer.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 0376918..fc13de1 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -261,8 +261,7 @@ read_plt(_State, Plt) -> {error, no_such_file} -> error; {error, not_valid} -> - Error = io_lib:format("Could not read the PLT file ~p", [Plt]), - throw({dialyzer_error, Error}); + error; {error, read_error} -> Error = io_lib:format("Could not read the PLT file ~p", [Plt]), throw({dialyzer_error, Error}) -- cgit v1.1 From a5bdf11770a8b57f7db2ca28f14dbf66c43673db Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Thu, 4 Aug 2016 15:06:47 +0300 Subject: Typo fix. --- src/rebar_prv_common_test.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index fbd0e89..1e0632e 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -221,7 +221,7 @@ select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> Configs = lists:flatmap(fun(Filename) -> rebar_file_utils:consult_config(State, Filename) end, SysConfigs), - [application:load(Application) || Config <- SysConfigs, {Application, _} <- Config], + [application:load(Application) || Config <- Configs, {Application, _} <- Config], rebar_utils:reread_config(Configs), Merged = lists:ukeymerge(1, -- cgit v1.1 From f9576c8598c4d44a3bd8adf36d62f5eb9b8c29a3 Mon Sep 17 00:00:00 2001 From: Nathaniel Waisbrot Date: Thu, 4 Aug 2016 14:36:46 -0400 Subject: Handle `escriptize` when the specified app is missing When rebar.config contains a `escript_main_app` option, but the specified app doesn't exist in the build directory, print an error. --- src/rebar_prv_escriptize.erl | 8 ++++++-- test/rebar_escriptize_SUITE.erl | 26 +++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl index d8704f6..6e10947 100644 --- a/src/rebar_prv_escriptize.erl +++ b/src/rebar_prv_escriptize.erl @@ -72,8 +72,12 @@ do(State) -> end; Name -> AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State), - {ok, AppInfo} = rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps), - escriptize(State, AppInfo) + case rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps) of + {ok, AppInfo} -> + escriptize(State, AppInfo); + _ -> + ?PRV_ERROR({bad_name, Name}) + end end. escriptize(State0, App) -> diff --git a/test/rebar_escriptize_SUITE.erl b/test/rebar_escriptize_SUITE.erl index 1817d6b..139d5cd 100644 --- a/test/rebar_escriptize_SUITE.erl +++ b/test/rebar_escriptize_SUITE.erl @@ -5,6 +5,8 @@ end_per_suite/1, init_per_testcase/2, all/0, + escriptize_with_name/1, + escriptize_with_bad_name/1, build_and_clean_app/1]). -include_lib("common_test/include/ct.hrl"). @@ -24,7 +26,11 @@ init_per_testcase(_, Config) -> rebar_test_utils:init_rebar_state(Config). all() -> - [build_and_clean_app]. + [ + build_and_clean_app, + escriptize_with_name, + escriptize_with_bad_name + ]. %% Test escriptize builds and runs the app's escript build_and_clean_app(Config) -> @@ -35,3 +41,21 @@ build_and_clean_app(Config) -> rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), rebar_test_utils:run_and_check(Config, [], ["escriptize"], {ok, [{app, Name, valid}]}). + +escriptize_with_name(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:run_and_check(Config, [{escript_main_app, Name}], ["escriptize"], + {ok, [{app, Name, valid}]}). + +escriptize_with_bad_name(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:run_and_check(Config, [{escript_main_app, boogers}], ["escriptize"], + {error,{rebar_prv_escriptize, {bad_name, boogers}}}). -- cgit v1.1 From bfd15ce03e9a197f0df68932ad9deb177b06c3fd Mon Sep 17 00:00:00 2001 From: Evgeny M Date: Tue, 9 Aug 2016 19:18:13 +0400 Subject: Ignore IntelliJ IDEA files This family of IDE with Erlang plugin is already quite popular. --- priv/templates/gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/templates/gitignore b/priv/templates/gitignore index ced0c5e..121a4de 100644 --- a/priv/templates/gitignore +++ b/priv/templates/gitignore @@ -13,3 +13,4 @@ erl_crash.dump .rebar logs _build +.idea -- cgit v1.1 From ea7942d9477bf2b0e3fffb905c04d330536f6c53 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Wed, 10 Aug 2016 13:10:55 -0400 Subject: Fix filtering of system libraries in escriptize https://github.com/erlang/rebar3/pull/1249 introduced a mechanism by which escript dependencies of applications only would be included; this required adding a filter to skip system libraries in the OTP root because that tends to break escripts in very nasty ways. However, the problem came that some libraries are just not in the escript path but may still be included; for these libraries the path prefix check failed as they return `{error, bad_name}` from `code:lib_dir(Dep)` rather than just the path they're in -- specifically, this happens with top level apps. The issue was reported in https://github.com/erlang/rebar3/issues/1294 and the current patch fixes it by accepting a `bad_name` dep as valid, since it is obviously not in the root path. --- src/rebar_prv_escriptize.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl index d8704f6..e92c80d 100644 --- a/src/rebar_prv_escriptize.erl +++ b/src/rebar_prv_escriptize.erl @@ -232,7 +232,9 @@ find_deps_of_deps([Name|Names], Apps, Acc) -> DepNames = proplists:get_value(applications, rebar_app_info:app_details(App), []), BinDepNames = [ec_cnv:to_binary(Dep) || Dep <- DepNames, %% ignore system libs; shouldn't include them. - not lists:prefix(code:root_dir(), code:lib_dir(Dep))] + DepDir <- [code:lib_dir(Dep)], + DepDir =:= {error, bad_name} orelse % those are all local + not lists:prefix(code:root_dir(), DepDir)] -- ([Name|Names]++Acc), % avoid already seen deps ?DEBUG("new deps of ~p found to be ~p", [Name, BinDepNames]), find_deps_of_deps(BinDepNames ++ Names, Apps, BinDepNames ++ Acc). -- cgit v1.1 From ef3e9e7aa2bcc103e064165d24881fd5d961ed70 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 13 Aug 2016 17:58:15 -0400 Subject: Make the escriptize provider hookable This will allow to move and modify the generated files --- src/rebar_prv_escriptize.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl index 06b54ed..7ee20c2 100644 --- a/src/rebar_prv_escriptize.erl +++ b/src/rebar_prv_escriptize.erl @@ -61,8 +61,11 @@ desc() -> "the project's and its dependencies' BEAM files.". do(State) -> + Providers = rebar_state:providers(State), + Cwd = rebar_state:dir(State), + rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State), ?INFO("Building escript...", []), - case rebar_state:get(State, escript_main_app, undefined) of + Res = case rebar_state:get(State, escript_main_app, undefined) of undefined -> case rebar_state:project_apps(State) of [App] -> @@ -78,7 +81,9 @@ do(State) -> _ -> ?PRV_ERROR({bad_name, Name}) end - end. + end, + rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State), + Res. escriptize(State0, App) -> AppName = rebar_app_info:name(App), -- cgit v1.1 From 5eb6b7dfdadd0d5b32597a722912f5faf82cad75 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 13 Aug 2016 18:09:51 -0400 Subject: Use hooks to generate rebar3 executable faster Repeats some of the steps used in the bootstrap script for non-windows systems so that 'rebar3 escriptize' can be used in development for faster results: - ./bootstrap 9.95s user 1.55s system 102% cpu 11.234 total - rebar3 escriptize 0.49s user 0.14s system 101% cpu 0.623 total --- rebar.config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rebar.config b/rebar.config index f91d61b..5cbc2be 100644 --- a/rebar.config +++ b/rebar.config @@ -12,6 +12,10 @@ {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. +{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", + escriptize, + "cp $REBAR_BUILD_DIR/bin/rebar3 ./rebar3 && chmod u+x rebar3"}]}. + {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. %% escript_incl_extra is for internal rebar-private use only. -- cgit v1.1 From 4c2873d213ccdb4182f21c7179038897a66210b2 Mon Sep 17 00:00:00 2001 From: Rasmus Svensson Date: Mon, 15 Aug 2016 21:20:14 +0200 Subject: Print error on too many help arguments Previously the help task would crash when given more than two arguments. After this change it instead print a message: Too many arguments given. Usage: rebar3 help [] --- src/rebar_prv_help.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rebar_prv_help.erl b/src/rebar_prv_help.erl index c028264..75cc609 100644 --- a/src/rebar_prv_help.erl +++ b/src/rebar_prv_help.erl @@ -41,7 +41,10 @@ do(State) -> [Name] -> % default namespace task_help(default, list_to_atom(Name), State); [Namespace, Name] -> - task_help(list_to_atom(Namespace), list_to_atom(Name), State) + task_help(list_to_atom(Namespace), list_to_atom(Name), State); + _ -> + {error, "Too many arguments given. " ++ + "Usage: rebar3 help [] "} end. -spec format_error(any()) -> iolist(). -- cgit v1.1 From 5d475eefa27ddbb7876d74a9a3f6f45267333d57 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 25 Aug 2016 08:56:44 -0400 Subject: Make Edoc carry paths of pre-built libraries Given the topological sort applied to top-level apps, we should be able to carry the edoc values for paths configured when more than one app exists. This allows multiple disjoint app to have defined cross-linking in the documentation. Tests pending. --- src/rebar_prv_edoc.erl | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl index 6cefe14..465fc34 100644 --- a/src/rebar_prv_edoc.erl +++ b/src/rebar_prv_edoc.erl @@ -33,17 +33,25 @@ do(State) -> code:add_pathsa(rebar_state:code_paths(State, all_deps)), ProjectApps = rebar_state:project_apps(State), Providers = rebar_state:providers(State), - EDocOpts = rebar_state:get(State, edoc_opts, []), + EdocOpts = rebar_state:get(State, edoc_opts, []), + ShouldAccPaths = not has_configured_paths(EdocOpts), Cwd = rebar_state:dir(State), rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - lists:foreach(fun(AppInfo) -> - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State), - AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)), - ?INFO("Running edoc for ~s", [AppName]), - AppDir = rebar_app_info:dir(AppInfo), - ok = edoc:application(list_to_atom(AppName), AppDir, EDocOpts), - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State) - end, ProjectApps), + lists:foldl(fun(AppInfo, EdocOptsAcc) -> + rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State), + AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)), + ?INFO("Running edoc for ~s", [AppName]), + AppDir = rebar_app_info:dir(AppInfo), + ok = edoc:application(list_to_atom(AppName), AppDir, EdocOptsAcc), + rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State), + case ShouldAccPaths of + true -> + %% edoc wants / on all OSes + add_to_paths(EdocOptsAcc, AppDir++"/doc"); + false -> + EdocOptsAcc + end + end, EdocOpts, ProjectApps), rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), {ok, State}. @@ -55,3 +63,12 @@ format_error(Reason) -> %% =================================================================== %% Internal functions %% =================================================================== +has_configured_paths(EdocOpts) -> + proplists:get_value(dir, EdocOpts) =/= undefined. + +add_to_paths([], Path) -> + [{doc_path, [Path]}]; +add_to_paths([{doc_path, Paths}|T], Path) -> + [{doc_path, [Path | Paths]} | T]; +add_to_paths([H|T], Path) -> + [H | add_to_paths(Path, T)]. -- cgit v1.1 From 2c78cfd557249166cdd264d976ca191eb8a5061f Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 25 Aug 2016 09:35:59 -0400 Subject: Add tests for multi-app edoc linking working --- test/rebar_edoc_SUITE.erl | 52 ++++++++++++++++++++++ .../foo/apps/bar1/src/bar1.app.src | 16 +++++++ .../foo/apps/bar1/src/bar1.erl | 9 ++++ .../foo/apps/bar1/src/bar1_app.erl | 26 +++++++++++ .../foo/apps/bar1/src/bar1_sup.erl | 35 +++++++++++++++ .../foo/apps/bar2/src/bar2.app.src | 16 +++++++ .../foo/apps/bar2/src/bar2.erl | 9 ++++ .../foo/apps/bar2/src/bar2_app.erl | 26 +++++++++++ .../foo/apps/bar2/src/bar2_sup.erl | 35 +++++++++++++++ .../foo/apps/foo/src/foo.app.src | 17 +++++++ .../rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl | 19 ++++++++ .../foo/apps/foo/src/foo_app.erl | 26 +++++++++++ .../foo/apps/foo/src/foo_sup.erl | 35 +++++++++++++++ 13 files changed, 321 insertions(+) create mode 100644 test/rebar_edoc_SUITE.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl create mode 100644 test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl diff --git a/test/rebar_edoc_SUITE.erl b/test/rebar_edoc_SUITE.erl new file mode 100644 index 0000000..fded2b0 --- /dev/null +++ b/test/rebar_edoc_SUITE.erl @@ -0,0 +1,52 @@ +-module(rebar_edoc_SUITE). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-compile(export_all). + +all() -> [multiapp]. + +init_per_testcase(multiapp, Config) -> + application:load(rebar), + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + Name = rebar_test_utils:create_random_name("multiapp"), + AppsDir = filename:join([PrivDir, rebar_test_utils:create_random_name(Name)]), + ec_file:copy(filename:join([DataDir, "foo"]), AppsDir, [recursive]), + Verbosity = rebar3:log_level(), + rebar_log:init(command_line, Verbosity), + State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])} + ,{root_dir, AppsDir}]), + [{apps, AppsDir}, {state, State}, {name, Name} | Config]. + +end_per_testcase(_, Config) -> + Config. + +multiapp(Config) -> + %% With an empty config (no `dir'), links are being processed + RebarConfig = [], + rebar_test_utils:run_and_check(Config, RebarConfig, ["edoc"], {ok, []}), + %% validate that all doc entries are generated and links work + AppsDir = ?config(apps, Config), + ct:pal("AppsDir: ~s", [AppsDir]), + ?assert(file_content_matches( + filename:join([AppsDir, "apps", "bar1", "doc", "bar1.html"]), + "barer1")), + ?assert(file_content_matches( + filename:join([AppsDir, "apps", "bar2", "doc", "bar2.html"]), + "barer2")), + %% Links are in place for types + ?assert(file_content_matches( + filename:join([AppsDir, "apps", "foo", "doc", "foo.html"]), + "barer1")), + ?assert(file_content_matches( + filename:join([AppsDir, "apps", "foo", "doc", "foo.html"]), + "apps/bar1/doc/bar1.html")). + + +file_content_matches(Path, Regex) -> + case file:read_file(Path) of + {ok, Bin} -> + nomatch =/= re:run(Bin, Regex); + {error, Reason} -> + Reason + end. diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src new file mode 100644 index 0000000..6e7ec24 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src @@ -0,0 +1,16 @@ +{application, bar1, + [{description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, { bar1_app, []}}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {maintainers, []}, + {licenses, []}, + {links, []} + ]}. diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl new file mode 100644 index 0000000..2700aef --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl @@ -0,0 +1,9 @@ +-module(bar1). +-export([bar1/0]). +-export_type([barer1/0]). + +-type barer1() :: string(). + +% @doc Bar1 bars the bar. +-spec bar1() -> barer1(). +bar1() -> "Barer1". \ No newline at end of file diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl new file mode 100644 index 0000000..414ac30 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%% @doc bar1 public API +%% @end +%%%------------------------------------------------------------------- + +-module(bar1_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%%==================================================================== +%% API +%%==================================================================== + +start(_StartType, _StartArgs) -> + bar1_sup:start_link(). + +%%-------------------------------------------------------------------- +stop(_State) -> + ok. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl new file mode 100644 index 0000000..f9d6670 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc bar1 top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(bar1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%==================================================================== +%% API functions +%%==================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%==================================================================== +%% Supervisor callbacks +%%==================================================================== + +%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} +init([]) -> + {ok, { {one_for_all, 0, 1}, []} }. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src new file mode 100644 index 0000000..58de8bc --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src @@ -0,0 +1,16 @@ +{application, bar2, + [{description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, { bar2_app, []}}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {maintainers, []}, + {licenses, []}, + {links, []} + ]}. diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl new file mode 100644 index 0000000..c639db0 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl @@ -0,0 +1,9 @@ +-module(bar2). +-export([bar2/0]). +-export_type([barer2/0]). + +-type barer2() :: string(). + +% @doc Bar2 bars the bar2. +-spec bar2() -> barer2(). +bar2() -> "Barer2". \ No newline at end of file diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl new file mode 100644 index 0000000..d0058a0 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%% @doc bar2 public API +%% @end +%%%------------------------------------------------------------------- + +-module(bar2_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%%==================================================================== +%% API +%%==================================================================== + +start(_StartType, _StartArgs) -> + bar2_sup:start_link(). + +%%-------------------------------------------------------------------- +stop(_State) -> + ok. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl new file mode 100644 index 0000000..0bdaf4a --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc bar2 top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(bar2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%==================================================================== +%% API functions +%%==================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%==================================================================== +%% Supervisor callbacks +%%==================================================================== + +%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} +init([]) -> + {ok, { {one_for_all, 0, 1}, []} }. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src new file mode 100644 index 0000000..9987fd5 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src @@ -0,0 +1,17 @@ +{application, foo, + [{description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, { foo_app, []}}, + {applications, + [kernel, + stdlib, + bar1, bar2 + ]}, + {env,[]}, + {modules, []}, + + {maintainers, []}, + {licenses, []}, + {links, []} + ]}. diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl new file mode 100644 index 0000000..52e3d0a --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl @@ -0,0 +1,19 @@ +-module(foo). + +-export([foo/0, bar1/0, bar2/0]). + +-export_type([fooer/0]). + +-type fooer() :: string(). + +% @doc Foo function returns fooer. +-spec foo() -> fooer(). +foo() -> "fooer". + +% @doc Bar1 function returns barer1. +-spec bar1() -> bar1:barer1(). +bar1() -> bar1:bar1(). + +% @doc Bar2 functions returns barer2. +-spec bar2() -> bar2:barer2(). +bar2() -> bar2:bar2(). \ No newline at end of file diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl new file mode 100644 index 0000000..d0158d7 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%% @doc foo public API +%% @end +%%%------------------------------------------------------------------- + +-module(foo_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%%==================================================================== +%% API +%%==================================================================== + +start(_StartType, _StartArgs) -> + foo_sup:start_link(). + +%%-------------------------------------------------------------------- +stop(_State) -> + ok. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl new file mode 100644 index 0000000..67e88b4 --- /dev/null +++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc foo top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(foo_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%==================================================================== +%% API functions +%%==================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%==================================================================== +%% Supervisor callbacks +%%==================================================================== + +%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} +init([]) -> + {ok, { {one_for_all, 0, 1}, []} }. + +%%==================================================================== +%% Internal functions +%%==================================================================== -- cgit v1.1 From b21dbaa0049b5aee67e8316ceee357d30e80a97a Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Thu, 25 Aug 2016 18:54:37 -0700 Subject: upgrade relx to 3.21.0 --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 5cbc2be..eabf3ac 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.0.4"}, - {relx, "3.20.0"}, + {relx, "3.21.0"}, {cf, "0.2.1"}, {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. diff --git a/rebar.lock b/rebar.lock index 231e266..23fb552 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,7 +7,7 @@ {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.20.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.21.0">>},0}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ @@ -19,6 +19,6 @@ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"B515B8317D25B3A1508699294C3D1FA6DC0527851DFFC87446661BCE21A36710">>}, + {<<"relx">>, <<"91E1EA9F09B4EDFDA8461901F4B5C5E0226E43EC161E147EEAB29F7761DF6EB5">>}, {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} ]. -- cgit v1.1 From 4f32da2ab7e0a7cf2f2e2de911c0c8ea0a7d8848 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 25 Aug 2016 22:21:07 -0400 Subject: Equivalent trim_all in bin split for <17.x The trim_all option used in binary:split/3 is not supported in 17.x. This patch makes an equivalent operation by eliminating empty split fragments. From the docs: trim Removes trailing empty parts of the result (as does trim in re:split/3. trim_all Removes all empty parts of the result. The new expression is therefore equivalent to the old one, but with the added benefit of compatibility. Fixes #1275 --- src/rebar_app_utils.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index d256cac..50c6314 100644 --- a/src/rebar_app_utils.erl +++ b/src/rebar_app_utils.erl @@ -182,7 +182,7 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> undefined -> get_package(PkgName, "0", State); <<"~>", Vsn/binary>> -> - [Vsn1] = binary:split(Vsn, [<<" ">>], [trim_all, global]), + [Vsn1] = [X || X <- binary:split(Vsn, [<<" ">>], [global]), X =/= <<>>], get_package(PkgName, Vsn1, State); _ -> {PkgName, PkgVsn} -- cgit v1.1 From ff4fd9f6f0c9989d1ff851c6201928f2a3f18e70 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 26 Aug 2016 13:01:22 -0400 Subject: Bumping to 3.3.0 --- bootstrap | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index 90775c8..e48aff4 100755 --- a/bootstrap +++ b/bootstrap @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.2.0", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.3.0", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence diff --git a/src/rebar.app.src b/src/rebar.app.src index 5b735cf..c844475 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.3.0"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 8ee9cc89969afd79bf8687dcdf978f1a09e01d48 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 26 Aug 2016 13:01:58 -0400 Subject: Returning to git-based tagging --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index c844475..5b735cf 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.3.0"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From dc03bb4c2d8f9dc4cb72590a02a20635e9526aa5 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 27 Aug 2016 07:32:09 -0400 Subject: Add test suite for cover_excl_mods option --- test/rebar_cover_SUITE.erl | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index 841e29f..1289f19 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -12,7 +12,8 @@ root_extra_src_dirs/1, index_written/1, flag_verbose/1, - config_verbose/1]). + config_verbose/1, + excl_mods/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -35,7 +36,8 @@ all() -> basic_extra_src_dirs, release_extra_src_dirs, root_extra_src_dirs, index_written, - flag_verbose, config_verbose]. + flag_verbose, config_verbose, + excl_mods]. flag_coverdata_written(Config) -> AppDir = ?config(apps, Config), @@ -206,3 +208,27 @@ config_verbose(Config) -> {ok, [{app, Name}]}), true = filelib:is_file(filename:join([AppDir, "_build", "test", "cover", "index.html"])). + +excl_mods(Config) -> + AppDir = ?config(apps, Config), + + Name1 = rebar_test_utils:create_random_name("relapp1_"), + Vsn1 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name1]), Name1, Vsn1, [kernel, stdlib]), + + Name2 = rebar_test_utils:create_random_name("relapp2_"), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(filename:join([AppDir, "apps", Name2]), Name2, Vsn2, [kernel, stdlib]), + + Mod1 = list_to_atom(Name1), + Mod2 = list_to_atom(Name2), + RebarConfig = [{erl_opts, [{d, some_define}]}, + {cover_excl_mods, [Mod2]}], + + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit", "--cover"], + {ok, [{app, Name1}, {app, Name2}]}), + + {file, _} = cover:is_compiled(Mod1), + false = cover:is_compiled(Mod2). -- cgit v1.1 From 4b0a07221de5e338cd71d774deff249f0528fa36 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 27 Aug 2016 07:48:06 -0400 Subject: Re-format cover exclusion code - brings back former error handling and debug messages - keeps the filtering of excluded mods and debug messages - breaks up code into multiple functions and removes nesting --- src/rebar_prv_cover.erl | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 9772e96..7c2597b 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -308,23 +308,16 @@ cover_compile(State, Dirs) -> lists:foreach(fun(Dir) -> case file:list_dir(Dir) of {ok, Files} -> - Files2 = [F || F <- Files, filename:extension(F) == ".beam"], - lists:foreach(fun(File) -> - case lists:any(fun (Excl) -> - File =:= (atom_to_list(Excl) ++ ".beam") - end, ExclMods) of - true -> - ?DEBUG("cover ignoring ~p ~p", [Dir, File]); - _ -> - ?DEBUG("cover compiling ~p ~p", [Dir, File]), - case catch(cover:compile_beam(filename:join(Dir, File))) of - {error, Reason} -> - ?WARN("Cover compilation failed: ~p", [Reason]); - {ok, _} -> - ok - end - end - end, Files2); + ?DEBUG("cover compiling ~p", [Dir]), + [cover_compile_file(filename:join(Dir, File)) + || File <- Files, + filename:extension(File) == ".beam", + not is_ignored(Dir, File, ExclMods)], + ok; + {error, eacces} -> + ?WARN("Directory ~p not readable, modules will not be included in coverage", [Dir]); + {error, enoent} -> + ?WARN("Directory ~p not found", [Dir]); {error, Reason} -> ?WARN("Directory ~p error ~p", [Dir, Reason]) end @@ -332,6 +325,22 @@ cover_compile(State, Dirs) -> rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), ok. +is_ignored(Dir, File, ExclMods) -> + Ignored = lists:any(fun(Excl) -> + File =:= atom_to_list(Excl) ++ ".beam" + end, + ExclMods), + Ignored andalso ?DEBUG("cover ignoring ~p ~p", [Dir, File]), + Ignored. + +cover_compile_file(FileName) -> + case catch(cover:compile_beam(FileName)) of + {error, Reason} -> + ?WARN("Cover compilation failed: ~p", [Reason]); + {ok, _} -> + ok + end. + app_dirs(Apps) -> lists:foldl(fun app_ebin_dirs/2, [], Apps). -- cgit v1.1 From 6bd8cda77aaa089f9eaa87dd696cfa63af5f2292 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 27 Aug 2016 15:56:01 -0400 Subject: Fix crash when doing hash check with missing index Specifically, when fetching an application where the expected hash is unknown, the hash is validated from the hex index; when the index is available, the hash is fetched fine and later inserted in the lock file. However, if the index is not available, the call would simply crash. This patch fixes thing so that instead, the index is refreshed before giving up and failing. --- src/rebar_app_utils.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index 50c6314..847b0a5 100644 --- a/src/rebar_app_utils.erl +++ b/src/rebar_app_utils.erl @@ -190,7 +190,7 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> %% store the expected hash for the dependency Hash1 = case Hash of undefined -> % unknown, define the hash since we know the dep - rebar_packages:registry_checksum({pkg, PkgName1, PkgVsn1, Hash}, State); + fetch_checksum(PkgName1, PkgVsn1, Hash, State); _ -> % keep as is Hash end, @@ -203,6 +203,15 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> update_source(AppInfo, Source, _State) -> rebar_app_info:source(AppInfo, Source). +fetch_checksum(PkgName, PkgVsn, Hash, State) -> + try + rebar_packages:registry_checksum({pkg, PkgName, PkgVsn, Hash}, State) + catch + _:_ -> + ?INFO("Package ~s-~s not found. Fetching registry updates and trying again...", [PkgName, PkgVsn]), + {ok, _} = rebar_prv_update:do(State), + rebar_packages:registry_checksum({pkg, PkgName, PkgVsn, Hash}, State) + end. format_error({missing_package, Package}) -> io_lib:format("Package not found in registry: ~s", [Package]); -- cgit v1.1 From 7e554d06978ac52a4e56b48f67819af356a3279b Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sat, 27 Aug 2016 13:22:41 -0700 Subject: reset accumulated coverdata on writing out to disk. this prevents provider chains like `eunit, ct, proper` from misreporting cover stats from providers later in the sequence --- src/rebar_prv_cover.erl | 2 ++ test/rebar_cover_SUITE.erl | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 7c2597b..401c331 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -376,6 +376,8 @@ write_coverdata(State, Task) -> ExportFile = filename:join([DataDir, atom_to_list(Task) ++ ".coverdata"]), case cover:export(ExportFile) of ok -> + %% dump accumulated coverdata after writing + ok = cover:reset(), ?DEBUG("Cover data written to ~p.", [ExportFile]); {error, Reason} -> ?WARN("Cover data export failed: ~p", [Reason]) diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index 1289f19..d66ec6f 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -13,7 +13,8 @@ index_written/1, flag_verbose/1, config_verbose/1, - excl_mods/1]). + excl_mods/1, + coverdata_is_reset_on_write/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -37,7 +38,7 @@ all() -> root_extra_src_dirs, index_written, flag_verbose, config_verbose, - excl_mods]. + excl_mods, coverdata_is_reset_on_write]. flag_coverdata_written(Config) -> AppDir = ?config(apps, Config), @@ -232,3 +233,19 @@ excl_mods(Config) -> {file, _} = cover:is_compiled(Mod1), false = cover:is_compiled(Mod2). + +coverdata_is_reset_on_write(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("coverdata_is_reset_on_write_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{d, some_define}]}, {cover_enabled, true}], + rebar_test_utils:run_and_check(Config, + RebarConfig, + ["eunit"], + {ok, [{app, Name}]}), + + {result, Ok, []} = cover:analyse(), + [] = lists:filter(fun({_, {0,_}}) -> false; (_) -> true end, Ok). -- cgit v1.1 From 5ef4b7bcbe026852eb42e10ccc6f75b8e8854a15 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sat, 27 Aug 2016 13:33:57 -0700 Subject: use `cover:analyse(cover:modules())` for tests instead of `cover:analyse()` `cover:analyse/0` didn't exist pre-otp18 --- test/rebar_cover_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index d66ec6f..84723ff 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -247,5 +247,5 @@ coverdata_is_reset_on_write(Config) -> ["eunit"], {ok, [{app, Name}]}), - {result, Ok, []} = cover:analyse(), + {result, Ok, []} = cover:analyse(cover:modules()), [] = lists:filter(fun({_, {0,_}}) -> false; (_) -> true end, Ok). -- cgit v1.1 From 3beeec9db0880c5e9f57a427796d9d9af9ffa46d Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Sat, 27 Aug 2016 13:44:33 -0700 Subject: r15 proof cover tests`` --- test/rebar_cover_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index 84723ff..4192f4a 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -247,5 +247,6 @@ coverdata_is_reset_on_write(Config) -> ["eunit"], {ok, [{app, Name}]}), - {result, Ok, []} = cover:analyse(cover:modules()), + Res = lists:map(fun(M) -> cover:analyse(M) end, cover:modules()), + Ok = lists:foldl(fun({ok, R}, Acc) -> R ++ Acc end, [], Res), [] = lists:filter(fun({_, {0,_}}) -> false; (_) -> true end, Ok). -- cgit v1.1 From d4b76391475ab5ddda80f7799bda3ee1745e8244 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Tue, 30 Aug 2016 12:17:52 +0200 Subject: Print stacktrace in a more conventional way Insert a newline before printing the stacktrace so that the term is easier to read and copy. This is a more conventional way to print traces, and is, for instance, the way it's done by make and python. --- src/rebar3.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar3.erl b/src/rebar3.erl index d3ea15f..c665f20 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -292,7 +292,7 @@ handle_error(Error) -> case erlang:get_stacktrace() of [] -> ok; Trace -> - ?DEBUG("Stack trace to the error location: ~p", [Trace]) + ?DEBUG("Stack trace to the error location:~n~p", [Trace]) end, ?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []), erlang:halt(1). -- cgit v1.1 From 4b69622d72046f26e17a9e3500a924219685032e Mon Sep 17 00:00:00 2001 From: vans163 Date: Thu, 1 Sep 2016 12:06:10 -0400 Subject: Update rebar_agent.erl https://github.com/erlang/rebar3/pull/1317 In reference to with support to load erlang code atomically but load nifs non-atomically. --- src/rebar_agent.erl | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 95818d8..5d6b050 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -109,8 +109,7 @@ refresh_paths(RState) -> {X,X} -> ?DEBUG("reloading ~p from ~s", [Modules, Path]), code:replace_path(App, Path), - [begin code:purge(M), code:delete(M), code:load_file(M) end - || M <- Modules]; + reload_modules(Modules); {_,_} -> ?DEBUG("skipping app ~p, stable copy required", [App]) end @@ -123,3 +122,31 @@ refresh_state(RState, _Dir) -> rebar3:init_config(), [fun(S) -> rebar_state:apply_profiles(S, rebar_state:current_profiles(RState)) end] ). + +reload_modules([]) -> noop; +reload_modules(Modules) -> + reload_modules(Modules, erlang:function_exported(code, prepare_loading, 1)). + +%% OTP 19 and later -- use atomic loading and ignore unloadable mods +reload_modules(Modules, true) -> + case code:prepare_loading(Modules) of + {ok, Prepared} -> + [code:purge(M) || M <- Modules], + code:finish_loading(Prepared); + + {error, ModRsns} -> + Blacklist = + (fun Error([], Acc) -> Acc; + Error([ {ModError, on_load_not_allowed} |T], Acc) -> + reload_modules([ModError], false), + Error(T, Acc); + Error([ {ModError, _} |T], Acc) -> + Error(T, [ModError|Acc]) + end)(ModRsns, []), + reload_modules(Modules -- Blacklist, true) + end; + +%% Older versions, use a more ad-hoc mechanism. Specifically has +%% a harder time dealing with NIFs. +reload_modules(Modules, false) -> + [begin code:purge(M), code:load_file(M) end || M <- Modules]. -- cgit v1.1 From d74cd14065bacf786bf4a463aa47715e80b412ec Mon Sep 17 00:00:00 2001 From: vans163 Date: Thu, 1 Sep 2016 13:31:23 -0400 Subject: Update rebar_agent.erl opps. Infinite loop fixed. --- src/rebar_agent.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 5d6b050..72bbe44 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -137,9 +137,9 @@ reload_modules(Modules, true) -> {error, ModRsns} -> Blacklist = (fun Error([], Acc) -> Acc; - Error([ {ModError, on_load_not_allowed} |T], Acc) -> - reload_modules([ModError], false), - Error(T, Acc); + Error([ {ModNif, on_load_not_allowed} |T], Acc) -> + reload_modules([ModNif], false), + Error(T, [ModNif|Acc]); Error([ {ModError, _} |T], Acc) -> Error(T, [ModError|Acc]) end)(ModRsns, []), -- cgit v1.1 From 9b64c60f8bbbac7686d0f8d1f56219c847d2c811 Mon Sep 17 00:00:00 2001 From: vans163 Date: Thu, 1 Sep 2016 14:20:48 -0400 Subject: comment and spacing fixup --- src/rebar_agent.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 72bbe44..818401d 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -125,7 +125,7 @@ refresh_state(RState, _Dir) -> reload_modules([]) -> noop; reload_modules(Modules) -> - reload_modules(Modules, erlang:function_exported(code, prepare_loading, 1)). + reload_modules(Modules, erlang:function_exported(code, prepare_loading, 1)). %% OTP 19 and later -- use atomic loading and ignore unloadable mods reload_modules(Modules, true) -> @@ -137,16 +137,15 @@ reload_modules(Modules, true) -> {error, ModRsns} -> Blacklist = (fun Error([], Acc) -> Acc; - Error([ {ModNif, on_load_not_allowed} |T], Acc) -> + Error([ {ModNif, on_load_not_allowed} |T], Acc) -> reload_modules([ModNif], false), Error(T, [ModNif|Acc]); - Error([ {ModError, _} |T], Acc) -> + Error([ {ModError, _} |T], Acc) -> Error(T, [ModError|Acc]) end)(ModRsns, []), reload_modules(Modules -- Blacklist, true) end; -%% Older versions, use a more ad-hoc mechanism. Specifically has -%% a harder time dealing with NIFs. +%% Older versions, use a more ad-hoc mechanism. reload_modules(Modules, false) -> [begin code:purge(M), code:load_file(M) end || M <- Modules]. -- cgit v1.1 From 6180ce0c64041e58feab600aa71ca31978b5d131 Mon Sep 17 00:00:00 2001 From: vans163 Date: Thu, 1 Sep 2016 14:44:38 -0400 Subject: Make less than R17 compatible --- src/rebar_agent.erl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 818401d..6b4526e 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -136,13 +136,17 @@ reload_modules(Modules, true) -> {error, ModRsns} -> Blacklist = - (fun Error([], Acc) -> Acc; - Error([ {ModNif, on_load_not_allowed} |T], Acc) -> - reload_modules([ModNif], false), - Error(T, [ModNif|Acc]); - Error([ {ModError, _} |T], Acc) -> - Error(T, [ModError|Acc]) - end)(ModRsns, []), + lists:foldr(fun({ModError, Error}, Acc) -> + case Error of + %perhaps cover other cases of failure? + on_load_not_allowed -> + reload_modules([ModError], false), + [ModError|Acc]; + _ -> [ModError|Acc] + end + end, + [], ModRsns + ), reload_modules(Modules -- Blacklist, true) end; -- cgit v1.1 From 0f524bb6c2efc687956af85150b322b3ed883b03 Mon Sep 17 00:00:00 2001 From: vans163 Date: Fri, 2 Sep 2016 10:12:39 -0400 Subject: delete purge load_file specific order --- src/rebar_agent.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 6b4526e..8e1c8f6 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -152,4 +152,4 @@ reload_modules(Modules, true) -> %% Older versions, use a more ad-hoc mechanism. reload_modules(Modules, false) -> - [begin code:purge(M), code:load_file(M) end || M <- Modules]. + [begin code:delete(M), code:purge(M), code:load_file(M) end || M <- Modules]. -- cgit v1.1 From ff752834ee292e05c67fee7e53a5e095afe0ce9f Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 2 Sep 2016 20:33:44 -0400 Subject: Bump to 3.3.1 --- bootstrap | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index e48aff4..2c9c66f 100755 --- a/bootstrap +++ b/bootstrap @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.3.0", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.3.1", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence diff --git a/src/rebar.app.src b/src/rebar.app.src index 5b735cf..d60df67 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.3.1"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 0e2b1a11b23658f06fb88076234eaad31605ff4c Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 2 Sep 2016 20:34:30 -0400 Subject: Back to semver version post release --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index d60df67..5b735cf 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.3.1"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 04589cf963028c75abd531892b1dcc54dae621b3 Mon Sep 17 00:00:00 2001 From: vans163 Date: Sat, 3 Sep 2016 10:35:26 -0400 Subject: log error if a module failed to load_file, the user should be aware --- src/rebar_agent.erl | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 8e1c8f6..2d83683 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -142,7 +142,9 @@ reload_modules(Modules, true) -> on_load_not_allowed -> reload_modules([ModError], false), [ModError|Acc]; - _ -> [ModError|Acc] + _ -> + ?ERROR("Module ~p failed to atomic load because ~p", [ModError, Error]), + [ModError|Acc] end end, [], ModRsns @@ -152,4 +154,13 @@ reload_modules(Modules, true) -> %% Older versions, use a more ad-hoc mechanism. reload_modules(Modules, false) -> - [begin code:delete(M), code:purge(M), code:load_file(M) end || M <- Modules]. + lists:foreach(fun(M) -> + code:delete(M), + code:purge(M), + case code:load_file(M) of + {module, M} -> ok; + {error, Error} -> + ?ERROR("Module ~p failed to load because ~p", [M, Error]) + end + end, Modules + ). \ No newline at end of file -- cgit v1.1 From 9ab25aaf1508e4286d755a0a60d6d22728a46dbf Mon Sep 17 00:00:00 2001 From: vans163 Date: Sat, 3 Sep 2016 15:56:38 -0400 Subject: replace error with debug --- src/rebar_agent.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 2d83683..4b0fc5f 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -143,7 +143,7 @@ reload_modules(Modules, true) -> reload_modules([ModError], false), [ModError|Acc]; _ -> - ?ERROR("Module ~p failed to atomic load because ~p", [ModError, Error]), + ?DEBUG("Module ~p failed to atomic load because ~p", [ModError, Error]), [ModError|Acc] end end, @@ -160,7 +160,7 @@ reload_modules(Modules, false) -> case code:load_file(M) of {module, M} -> ok; {error, Error} -> - ?ERROR("Module ~p failed to load because ~p", [M, Error]) + ?DEBUG("Module ~p failed to load because ~p", [M, Error]) end end, Modules ). \ No newline at end of file -- cgit v1.1 From 2b6fa7a25e0acdee4ce2a452152b688f2df27dea Mon Sep 17 00:00:00 2001 From: David de Boer Date: Wed, 14 Sep 2016 16:32:16 +0200 Subject: Ignore mv warnings In some cases, mv will throw a warning, while still moving the files correctly and returning a 0 return code: "mv: can't preserve ownership of ... Permission denied". --- src/rebar_file_utils.erl | 11 ++++++++--- test/rebar_file_utils_SUITE.erl | 12 ++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 104c047..437780d 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -171,9 +171,14 @@ mv(Source, Dest) -> {unix, _} -> EscSource = rebar_utils:escape_chars(Source), EscDest = rebar_utils:escape_chars(Dest), - {ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), - [{use_stdout, false}, abort_on_error]), - ok; + case rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), + [{use_stdout, false}, abort_on_error]) of + {ok, []} -> + ok; + {ok, Warning} -> + ?WARN("mv: ~p", [Warning]), + ok + end; {win32, _} -> Cmd = case filelib:is_dir(Source) of true -> diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl index a44a06d..7285e13 100644 --- a/test/rebar_file_utils_SUITE.erl +++ b/test/rebar_file_utils_SUITE.erl @@ -14,7 +14,8 @@ path_from_ancestor/1, canonical_path/1, resolve_link/1, - split_dirname/1]). + split_dirname/1, + mv_warning_is_ignored/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -27,7 +28,8 @@ all() -> path_from_ancestor, canonical_path, resolve_link, - split_dirname]. + split_dirname, + mv_warning_is_ignored]. groups() -> [{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]}, @@ -135,3 +137,9 @@ split_dirname(_Config) -> ?assertEqual({".", "foo"}, rebar_file_utils:split_dirname("foo")), ?assertEqual({"/foo", "bar"}, rebar_file_utils:split_dirname("/foo/bar")), ?assertEqual({"foo", "bar"}, rebar_file_utils:split_dirname("foo/bar")). + +mv_warning_is_ignored(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, sh, fun("mv ding dong", _) -> {ok, "Warning"} end), + ok = rebar_file_utils:mv("ding", "dong"), + meck:unload(rebar_utils). -- cgit v1.1 From 7b8ce5cc9a869f447a05d7f35697e426159ccec9 Mon Sep 17 00:00:00 2001 From: soranoba Date: Sat, 17 Sep 2016 18:48:26 +0900 Subject: update bbmustache 1.3.0 --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index eabf3ac..0344032 100644 --- a/rebar.config +++ b/rebar.config @@ -6,7 +6,7 @@ {certifi, "0.4.0"}, {providers, "1.6.0"}, {getopt, "0.8.2"}, - {bbmustache, "1.0.4"}, + {bbmustache, "1.3.0"}, {relx, "3.21.0"}, {cf, "0.2.1"}, {cth_readable, "1.2.3"}, diff --git a/rebar.lock b/rebar.lock index 23fb552..025c9fb 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,5 +1,5 @@ {"1.1.0", -[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0}, +[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.3.0">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0}, @@ -11,7 +11,7 @@ {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ - {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>}, + {<<"bbmustache">>, <<"2010ADAE78830992A4C69680115ECD7D475DD03A72C076BBADDCCBF2D4B32035">>}, {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, -- cgit v1.1 From c0a903bbf9b74df01fad71bbf9d267d1aa7cff4c Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 20 Sep 2016 00:35:46 -0700 Subject: cover compile prior to calculating coverage fixes #1327 --- src/rebar_prv_cover.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 401c331..968a632 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -15,7 +15,7 @@ -include("rebar.hrl"). -define(PROVIDER, cover). --define(DEPS, [app_discovery]). +-define(DEPS, [compile]). %% =================================================================== %% Public API @@ -84,6 +84,11 @@ reset(State) -> {ok, State}. analyze(State) -> + %% modules have to be cover compiled in order for + %% cover data to be reloaded + %% this maybe breaks if modules have been deleted + %% since code coverage was collected? + ok = cover_compile(State, apps), ?INFO("Performing cover analysis...", []), %% figure out what coverdata we have CoverDir = cover_dir(State), -- cgit v1.1 From f653d4dffec599f0d488aef30430e9761fc8955b Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 20 Sep 2016 00:47:16 -0700 Subject: only compile/cover compile when generating analysis don't compile when resetting coverdata --- src/rebar_prv_cover.erl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 968a632..b62a796 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -15,7 +15,7 @@ -include("rebar.hrl"). -define(PROVIDER, cover). --define(DEPS, [compile]). +-define(DEPS, [lock]). %% =================================================================== %% Public API @@ -84,11 +84,20 @@ reset(State) -> {ok, State}. analyze(State) -> - %% modules have to be cover compiled in order for - %% cover data to be reloaded + %% modules have to be compiled and then cover compiled + %% in order for cover data to be reloaded %% this maybe breaks if modules have been deleted %% since code coverage was collected? - ok = cover_compile(State, apps), + case rebar_prv_compile:do(State) of + %% successfully compiled apps + {ok, S} -> + ok = cover_compile(S, apps), + do_analyze(State); + %% this should look like a compiler error, not a cover error + Error -> Error + end. + +do_analyze(State) -> ?INFO("Performing cover analysis...", []), %% figure out what coverdata we have CoverDir = cover_dir(State), -- cgit v1.1 From dad7900d31fc2744c9d6b877f1a859f1f940cc39 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 20 Sep 2016 02:27:41 -0700 Subject: recompile all files when a parse transform given as an opt needs updating there's no way to detect which files actually rely on a parse transform passed to the compiler via the options (as opposed to `-compile(..)` so if any parse transforms are in modules that need recompiling just recompile the world fixes #1328 --- src/rebar_erlc_compiler.erl | 16 ++++++- test/rebar_compile_SUITE.erl | 105 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 167f2bb..b148172 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -202,7 +202,13 @@ compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> G = init_erlcinfo(include_abs_dirs(ErlOpts, BaseDir), AllErlFiles, BaseDir, OutDir), - NeededErlFiles = needed_files(G, ErlOpts, BaseDir, OutDir, AllErlFiles), + {ParseTransforms, Rest} = split_source_files(AllErlFiles, ErlOpts), + NeededErlFiles = case needed_files(G, ErlOpts, BaseDir, OutDir, ParseTransforms) of + [] -> needed_files(G, ErlOpts, BaseDir, OutDir, Rest); + %% at least one parse transform in the opts needs updating, so recompile all + _ -> AllErlFiles + end, + {ErlFirstFiles, ErlOptsFirst} = erl_first_files(RebarOpts, ErlOpts, BaseDir, NeededErlFiles), {DepErls, OtherErls} = lists:partition( fun(Source) -> digraph:in_degree(G, Source) > 0 end, @@ -296,6 +302,14 @@ erl_first_files(Opts, ErlOpts, Dir, NeededErlFiles) -> end, ErlOpts), {ErlFirstFiles ++ ParseTransformsErls, ErlOptsFirst}. +split_source_files(SourceFiles, ErlOpts) -> + ParseTransforms = proplists:get_all_values(parse_transform, ErlOpts), + lists:partition(fun(Source) -> + lists:member(filename_to_atom(Source), ParseTransforms) + end, SourceFiles). + +filename_to_atom(F) -> list_to_atom(filename:rootname(filename:basename(F))). + %% Get subset of SourceFiles which need to be recompiled, respecting %% dependencies induced by given graph G. needed_files(G, ErlOpts, Dir, OutDir, SourceFiles) -> diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index d9b75e4..cb16304 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -42,7 +42,9 @@ deps_build_in_prod/1, include_file_relative_to_working_directory/1, include_file_in_src/1, - always_recompile_when_erl_compiler_options_set/1]). + always_recompile_when_erl_compiler_options_set/1, + recompile_when_parse_transform_inline_changes/1, + recompile_when_parse_transform_as_opt_changes/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -65,7 +67,9 @@ all() -> parse_transform_test, erl_first_files_test, mib_test, umbrella_mib_first_test, only_default_transitive_deps, clean_all, override_deps, profile_override_deps, deps_build_in_prod, - include_file_relative_to_working_directory, include_file_in_src] ++ + include_file_relative_to_working_directory, include_file_in_src, + recompile_when_parse_transform_as_opt_changes, + recompile_when_parse_transform_inline_changes] ++ case erlang:function_exported(os, unsetenv, 1) of true -> [always_recompile_when_erl_compiler_options_set]; false -> [] @@ -1348,5 +1352,102 @@ always_recompile_when_erl_compiler_options_set(Config) -> _ -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv) end. +recompile_when_parse_transform_inline_changes(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("parse_transform_inline_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ok = filelib:ensure_dir(filename:join([AppDir, "src", "dummy"])), + + ModSrc = <<"-module(example).\n" + "-export([foo/2]).\n" + "-compile([{parse_transform, example_parse_transform}]).\n" + "foo(_, _) -> ok.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example.erl"]), + ModSrc), + + ParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> AST.\n">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + ParseTransform), + + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), + + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + {ok, Files} = rebar_utils:list_dir(EbinDir), + ModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- Files, filename:basename(F, ".beam") == "example"], + + timer:sleep(1000), + + NewParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> identity(AST).\n" + "identity(AST) -> AST.\n">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + NewParseTransform), + + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), + + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), + NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- NewFiles, filename:basename(F, ".beam") == "example"], + ?assert(ModTime =/= NewModTime). + +recompile_when_parse_transform_as_opt_changes(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("parse_transform_opt_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ok = filelib:ensure_dir(filename:join([AppDir, "src", "dummy"])), + + ModSrc = <<"-module(example).\n" + "-export([foo/2]).\n" + "foo(_, _) -> ok.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example.erl"]), + ModSrc), + + ParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> AST.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + ParseTransform), + + RebarConfig = [{erl_opts, [{parse_transform, example_parse_transform}]}], + + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), + + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + {ok, Files} = rebar_utils:list_dir(EbinDir), + ModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- Files, filename:basename(F, ".beam") == "example"], + + timer:sleep(1000), + + NewParseTransform = <<"-module(example_parse_transform).\n" + "-export([parse_transform/2]).\n" + "parse_transform(AST, _) -> identity(AST).\n" + "identity(AST) -> AST.">>, + + ok = file:write_file(filename:join([AppDir, "src", "example_parse_transform.erl"]), + NewParseTransform), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), + + {ok, NewFiles} = rebar_utils:list_dir(EbinDir), + NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) + || F <- NewFiles, filename:basename(F, ".beam") == "example"], + + ?assert(ModTime =/= NewModTime). -- cgit v1.1 From b79e5da2363114de34ce612f32a109d37e7d01ac Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Tue, 20 Sep 2016 21:28:01 -0700 Subject: allow using an alternate regex to locate test modules during eunit runs {`eunit_test_regex`, Regex}` will use the supplied `Regex` instead of the default to locate tests in test dirs. note this matches only the filename, not the path. the regex is applied to all test dirs, recursively fixes #1331 --- src/rebar_prv_eunit.erl | 19 ++++++++++++------- test/rebar_eunit_SUITE.erl | 26 +++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 942fd10..82e2458 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -18,6 +18,8 @@ %% we need to modify app_info state before compile -define(DEPS, [lock]). +-define(DEFAULT_TEST_REGEX, "^[^._].*\\.erl\$"). + %% =================================================================== %% Public API %% =================================================================== @@ -174,21 +176,24 @@ set_apps([App|Rest], Acc) -> set_modules(Apps, State) -> set_modules(Apps, State, {[], []}). set_modules([], State, {AppAcc, TestAcc}) -> - TestSrc = gather_src([filename:join([rebar_state:dir(State), "test"])]), + Regex = rebar_state:get(State, eunit_test_regex, ?DEFAULT_TEST_REGEX), + BareTestDir = [filename:join([rebar_state:dir(State), "test"])], + TestSrc = gather_src(BareTestDir, Regex), dedupe_tests({AppAcc, TestAcc ++ TestSrc}); set_modules([App|Rest], State, {AppAcc, TestAcc}) -> F = fun(Dir) -> filename:join([rebar_app_info:dir(App), Dir]) end, AppDirs = lists:map(F, rebar_dir:src_dirs(rebar_app_info:opts(App), ["src"])), - AppSrc = gather_src(AppDirs), + Regex = rebar_state:get(State, eunit_test_regex, ?DEFAULT_TEST_REGEX), + AppSrc = gather_src(AppDirs, Regex), TestDirs = [filename:join([rebar_app_info:dir(App), "test"])], - TestSrc = gather_src(TestDirs), + TestSrc = gather_src(TestDirs, Regex), set_modules(Rest, State, {AppSrc ++ AppAcc, TestSrc ++ TestAcc}). -gather_src(Dirs) -> gather_src(Dirs, []). +gather_src(Dirs, Regex) -> gather_src(Dirs, Regex, []). -gather_src([], Srcs) -> Srcs; -gather_src([Dir|Rest], Srcs) -> - gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, "^[^._].*\\.erl\$", true)). +gather_src([], _Regex, Srcs) -> Srcs; +gather_src([Dir|Rest], Regex, Srcs) -> + gather_src(Rest, Regex, Srcs ++ rebar_utils:find_files(Dir, Regex, true)). dedupe_tests({AppMods, TestMods}) -> %% for each modules in TestMods create a test if there is not a module diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl index 41ab6ff..6fb325b 100644 --- a/test/rebar_eunit_SUITE.erl +++ b/test/rebar_eunit_SUITE.erl @@ -18,6 +18,7 @@ -export([misspecified_eunit_tests/1]). -export([misspecified_eunit_compile_opts/1]). -export([misspecified_eunit_first_files/1]). +-export([alternate_test_regex/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -27,7 +28,8 @@ all() -> [{group, basic_app}, {group, multi_app}, {group, cmd_line_args}, misspecified_eunit_tests, misspecified_eunit_compile_opts, - misspecified_eunit_first_files]. + misspecified_eunit_first_files, + alternate_test_regex]. groups() -> [{basic_app, [sequence], [basic_app_compiles, {group, basic_app_results}]}, @@ -579,3 +581,25 @@ misspecified_eunit_first_files(Config) -> {error, {rebar_prv_eunit, Error}} = rebar_test_utils:run_and_check(State, RebarConfig, ["eunit"], return), {badconfig, {"Value `~p' of option `~p' must be a list", {some_file, eunit_first_files}}} = Error. + +alternate_test_regex(Config) -> + State = rebar_test_utils:init_rebar_state(Config, "alternate_test_regex_"), + + AppDir = ?config(apps, State), + PrivDir = ?config(priv_dir, State), + + AppDirs = ["src", "include", "test"], + + lists:foreach(fun(F) -> ec_file:copy(filename:join([PrivDir, "basic_app", F]), + filename:join([AppDir, F]), + [recursive]) end, AppDirs), + + BaseConfig = [{erl_opts, [{d, config_define}]}, {eunit_compile_opts, [{d, eunit_compile_define}]}], + + RebarConfig = [{eunit_test_regex, "basic_app_tests.erl"}|BaseConfig], + + {ok, S} = rebar_test_utils:run_and_check(State, RebarConfig, ["as", "test", "lock"], return), + + Set = {ok, [{application, basic_app}, + {module, basic_app_tests}]}, + Set = rebar_prv_eunit:prepare_tests(S). \ No newline at end of file -- cgit v1.1 From 384e9e58dba9ff8cf801bfdbe3c2ec406453aa5f Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 27 Sep 2016 10:23:47 -0400 Subject: Properly support package aliasing and alt names Aliasing only had a bit of ad-hoc support in rebar3, and various issues have encountered problems related to the package names not mapping properly with the application name. One such issue is https://github.com/erlang/rebar3/issues/1290 The problem has been hard to find because it only impacts transitive dependencies (not top-level ones) of other packages. The root cause for this is that the application name was not being tracked by rebar3's internal index, only the package name and its version were. When a given application was a package app, the data for the application name would be reconstructed from the lock file, but only if it were a top-level app or a dependency of a source application where parsing the lock file is necessary to know what comes next. When a transitive dependency of a package dependency was fetched, we instead read its dependencies directly from the in-memory package index within rebar3. This caused us to only read the package name and version, and lost all information regarding application name. This worked fine for most cases since for the vast majority of packages, the package name matches the app name, but failed for all aliases, which would then be moved to directories that wouldn't match the app name. This in turn broke some aspects of code analysis (in Dialyzer), or other functionality relying on static paths, such as including .hrl files from dependencies. This patch reformats the internal storage format of dependencies to align with the internal one used by rebar3, so that the app name can be carried along with the package name and its version. The fix can only work once `rebar3 update` is called so the index is rebuilt internally, and will the file cached on disk will be incompatible with older rebar3 versions. Currently, the following is not covered: - Tests - Including the package hashes of dependencies so they may match what is in a lock file -- they're being `undefined` instead, which may break some lookups. The previous format did not lend itself to hashing in the same way, and it is possible transitive deps were not being tracked properly, or worked by respecting the current package hierarchy. This will require further analysis For now this commit can allow reviewing and discussion. --- src/rebar_prv_update.erl | 26 +++++++++++++------------- test/rebar_deps_SUITE.erl | 16 ++++++++-------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 54b135e..ff7194a 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -142,8 +142,8 @@ hex_to_index(State) -> end. update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) -> - lists:foldl(fun([Dep, DepVsn, false, _AppName | _], DepsListAcc) -> - Dep1 = {Pkg, PkgVsn, Dep}, + lists:foldl(fun([Dep, DepVsn, false, AppName | _], DepsListAcc) -> + Dep1 = {Pkg, PkgVsn, Dep, AppName}, case {valid_vsn(DepVsn), DepVsn} of %% Those are all not perfectly implemented! %% and doubled since spaces seem not to be @@ -168,9 +168,9 @@ update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) -> cmpl(Dep1, rm_ws(Vsn), HexRegistry, State, DepsListAcc, fun ec_semver:lt/2); {_, <<"==", Vsn/binary>>} -> - [{Dep, Vsn} | DepsListAcc]; + [{AppName, {pkg, Dep, Vsn, undefined}} | DepsListAcc]; {_, Vsn} -> - [{Dep, Vsn} | DepsListAcc] + [{AppName, {pkg, Dep, Vsn, undefined}} | DepsListAcc] end; ([_Dep, _DepVsn, true, _AppName | _], DepsListAcc) -> DepsListAcc @@ -188,27 +188,27 @@ valid_vsn(Vsn) -> SupportedVersions = "^(>=?|<=?|~>|==)?\\s*" ++ SemVerRegExp ++ "$", re:run(Vsn, SupportedVersions) =/= nomatch. -highest_matching({Pkg, PkgVsn, Dep}, Vsn, HexRegistry, State, DepsListAcc) -> +highest_matching({Pkg, PkgVsn, Dep, App}, Vsn, HexRegistry, State, DepsListAcc) -> case rebar_packages:find_highest_matching(Pkg, PkgVsn, Dep, Vsn, HexRegistry, State) of {ok, HighestDepVsn} -> - [{Dep, HighestDepVsn} | DepsListAcc]; + [{App, {pkg, Dep, HighestDepVsn, undefined}} | DepsListAcc]; none -> ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", [Pkg, PkgVsn, Dep]), DepsListAcc end. -cmp({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) -> +cmp({_Pkg, _PkgVsn, Dep, _App} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) -> {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State), cmp_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun). -cmp_(undefined, _MinVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) -> +cmp_(undefined, _MinVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep, _App}, _CmpFun) -> ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", [Pkg, PkgVsn, Dep]), DepsListAcc; -cmp_(HighestDepVsn, _MinVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) -> - [{Dep, HighestDepVsn} | DepsListAcc]; +cmp_(HighestDepVsn, _MinVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep, App}, _CmpFun) -> + [{App, {pkg, Dep, HighestDepVsn, undefined}} | DepsListAcc]; cmp_(BestMatch, MinVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) -> case CmpFun(Vsn, MinVsn) of @@ -224,13 +224,13 @@ cmpl({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State), cmpl_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun). -cmpl_(undefined, _MaxVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) -> +cmpl_(undefined, _MaxVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep, _App}, _CmpFun) -> ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", [Pkg, PkgVsn, Dep]), DepsListAcc; -cmpl_(HighestDepVsn, _MaxVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) -> - [{Dep, HighestDepVsn} | DepsListAcc]; +cmpl_(HighestDepVsn, _MaxVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep, App}, _CmpFun) -> + [{App, {pkg, Dep, HighestDepVsn, undefined}} | DepsListAcc]; cmpl_(undefined, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) -> case CmpFun(Vsn, MaxVsn) of diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl index 24bf2a0..6b2ecea 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -385,36 +385,36 @@ https_os_proxy_settings(_Config) -> semver_matching_lt(_Config) -> Dep = <<"test">>, - Dep1 = {Dep, <<"1.0.0">>, Dep}, + Dep1 = {Dep, <<"1.0.0">>, Dep, Dep}, MaxVsn = <<"0.2.0">>, Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>], - ?assertEqual([{Dep, <<"0.1.9">>}], + ?assertEqual([{Dep, {pkg, Dep, <<"0.1.9">>, undefined}}], rebar_prv_update:cmpl_(undefined, MaxVsn, Vsns, [], Dep1, fun ec_semver:lt/2)). semver_matching_lte(_Config) -> Dep = <<"test">>, - Dep1 = {Dep, <<"1.0.0">>, Dep}, + Dep1 = {Dep, <<"1.0.0">>, Dep, Dep}, MaxVsn = <<"0.2.0">>, Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>], - ?assertEqual([{Dep, <<"0.2.0">>}], + ?assertEqual([{Dep, {pkg, Dep, <<"0.2.0">>, undefined}}], rebar_prv_update:cmpl_(undefined, MaxVsn, Vsns, [], Dep1, fun ec_semver:lte/2)). semver_matching_gt(_Config) -> Dep = <<"test">>, - Dep1 = {Dep, <<"1.0.0">>, Dep}, + Dep1 = {Dep, <<"1.0.0">>, Dep, Dep}, MaxVsn = <<"0.2.0">>, Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>], - ?assertEqual([{Dep, <<"0.2.1">>}], + ?assertEqual([{Dep, {pkg, Dep, <<"0.2.1">>, undefined}}], rebar_prv_update:cmp_(undefined, MaxVsn, Vsns, [], Dep1, fun ec_semver:gt/2)). semver_matching_gte(_Config) -> Dep = <<"test">>, - Dep1 = {Dep, <<"1.0.0">>, Dep}, + Dep1 = {Dep, <<"1.0.0">>, Dep, Dep}, MaxVsn = <<"0.2.0">>, Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>], - ?assertEqual([{Dep, <<"0.2.0">>}], + ?assertEqual([{Dep, {pkg, Dep, <<"0.2.0">>, undefined}}], rebar_prv_update:cmp_(undefined, MaxVsn, Vsns, [], Dep1, fun ec_semver:gt/2)). -- cgit v1.1 From 3f0e56d9c8c6da6c304543e47ff83e101aabf847 Mon Sep 17 00:00:00 2001 From: Brujo Benavides Date: Wed, 28 Sep 2016 19:32:22 -0300 Subject: Add support for behaviors, and not just behaviours --- src/rebar_erlc_compiler.erl | 2 ++ src/rebar_prv_xref.erl | 3 ++- test/rebar_xref_SUITE.erl | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index b148172..e6f2b71 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -668,6 +668,8 @@ process_attr(include_lib, Form, Includes, Dir) -> [FileNode] = erl_syntax:attribute_arguments(Form), RawFile = erl_syntax:string_value(FileNode), maybe_expand_include_lib_path(RawFile, Dir) ++ Includes; +process_attr(behavior, Form, Includes, _Dir) -> + process_attr(behaviour, Form, Includes, _Dir); process_attr(behaviour, Form, Includes, _Dir) -> [FileNode] = erl_syntax:attribute_arguments(Form), File = module_to_erl(erl_syntax:atom_value(FileNode)), diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl index 45badd3..3d74c9a 100644 --- a/src/rebar_prv_xref.erl +++ b/src/rebar_prv_xref.erl @@ -165,7 +165,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) -> []. diff --git a/test/rebar_xref_SUITE.erl b/test/rebar_xref_SUITE.erl index 75d6786..09f73a7 100644 --- a/test/rebar_xref_SUITE.erl +++ b/test/rebar_xref_SUITE.erl @@ -150,7 +150,7 @@ get_module_body(mymod, AppName, IgnoreXref) -> ["-ignore_xref([{other2,1},{localfunc2,0},{fdeprecated,0}]).\n" || X <- [IgnoreXref], X =:= true], "-behaviour(", AppName, "_behaviour1).\n", % 2 behaviours - "-behaviour(", AppName, "_behaviour2).\n", + "-behavior(", AppName, "_behaviour2).\n", "-deprecated({fdeprecated,0}).\n", % deprecated function "bh1_a(A) -> localfunc1(bh1_a, A).\n", % behaviour functions "bh1_b(A) -> localfunc1(bh1_b, A).\n", -- cgit v1.1 From 787cd967b632bef4534ade58ab64a51eda838df1 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 30 Sep 2016 09:27:40 -0400 Subject: Fix private includes when compiling in test profile When an include file is set in a private path (i.e. src/), the rebar3 compiler would not add them to the {i, Path} params -- only include/ and the project root were being added. This meant that when some extra source directories were added to the compile job, such as test/ when running under the test profile, the private include paths could not be shared with the test module. This patch fixes the issues (and adds tests) for such a specific case by adding all the configured include paths to the {i, Path} erl_opts arguments, yielding successful compile runs. --- src/rebar_erlc_compiler.erl | 23 ++++++++------ test/rebar_compile_SUITE.erl | 71 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index e6f2b71..bc29939 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -203,8 +203,8 @@ compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> G = init_erlcinfo(include_abs_dirs(ErlOpts, BaseDir), AllErlFiles, BaseDir, OutDir), {ParseTransforms, Rest} = split_source_files(AllErlFiles, ErlOpts), - NeededErlFiles = case needed_files(G, ErlOpts, BaseDir, OutDir, ParseTransforms) of - [] -> needed_files(G, ErlOpts, BaseDir, OutDir, Rest); + NeededErlFiles = case needed_files(G, ErlOpts, RebarOpts, BaseDir, OutDir, ParseTransforms) of + [] -> needed_files(G, ErlOpts, RebarOpts, BaseDir, OutDir, Rest); %% at least one parse transform in the opts needs updating, so recompile all _ -> AllErlFiles end, @@ -224,7 +224,7 @@ compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> true -> ErlOptsFirst; false -> ErlOpts end, - internal_erl_compile(C, BaseDir, S, OutDir, ErlOpts1) + internal_erl_compile(C, BaseDir, S, OutDir, ErlOpts1, RebarOpts) end) after true = digraph:delete(SubGraph), @@ -312,13 +312,15 @@ filename_to_atom(F) -> list_to_atom(filename:rootname(filename:basename(F))). %% Get subset of SourceFiles which need to be recompiled, respecting %% dependencies induced by given graph G. -needed_files(G, ErlOpts, Dir, OutDir, SourceFiles) -> +needed_files(G, ErlOpts, RebarOpts, Dir, OutDir, SourceFiles) -> lists:filter(fun(Source) -> TargetBase = target_base(OutDir, Source), Target = TargetBase ++ ".beam", + PrivIncludes = [{i, filename:join(Dir, Src)} + || Src <- rebar_dir:src_dirs(RebarOpts, ["src"])], AllOpts = [{outdir, filename:dirname(Target)} ,{i, filename:join(Dir, "include")} - ,{i, Dir}] ++ ErlOpts, + ,{i, Dir}] ++ PrivIncludes ++ ErlOpts, digraph:vertex(G, Source) > {Source, filelib:last_modified(Target)} orelse opts_changed(AllOpts, TargetBase) orelse erl_compiler_opts_set() @@ -518,12 +520,15 @@ expand_file_names(Files, Dirs) -> end, Files). -spec internal_erl_compile(rebar_dict(), file:filename(), file:filename(), - file:filename(), list()) -> ok | {ok, any()} | {error, any(), any()}. -internal_erl_compile(Opts, Dir, Module, OutDir, ErlOpts) -> + file:filename(), list(), rebar_dict()) -> + ok | {ok, any()} | {error, any(), any()}. +internal_erl_compile(Opts, Dir, Module, OutDir, ErlOpts, RebarOpts) -> Target = target_base(OutDir, Module) ++ ".beam", ok = filelib:ensure_dir(Target), - AllOpts = [{outdir, filename:dirname(Target)}] ++ ErlOpts ++ - [{i, filename:join(Dir, "include")}, {i, Dir}, return], + PrivIncludes = [{i, filename:join(Dir, Src)} + || Src <- rebar_dir:src_dirs(RebarOpts, ["src"])], + AllOpts = [{outdir, filename:dirname(Target)}] ++ ErlOpts ++ PrivIncludes ++ + [{i, filename:join(Dir, "include")}, {i, Dir}, return], case compile:file(Module, AllOpts) of {ok, _Mod} -> ok; diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index cb16304..f31ab39 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -42,6 +42,8 @@ deps_build_in_prod/1, include_file_relative_to_working_directory/1, include_file_in_src/1, + include_file_relative_to_working_directory_test/1, + include_file_in_src_test/1, always_recompile_when_erl_compiler_options_set/1, recompile_when_parse_transform_inline_changes/1, recompile_when_parse_transform_as_opt_changes/1]). @@ -68,6 +70,7 @@ all() -> umbrella_mib_first_test, only_default_transitive_deps, clean_all, override_deps, profile_override_deps, deps_build_in_prod, include_file_relative_to_working_directory, include_file_in_src, + include_file_relative_to_working_directory_test, include_file_in_src_test, recompile_when_parse_transform_as_opt_changes, recompile_when_parse_transform_inline_changes] ++ case erlang:function_exported(os, unsetenv, 1) of @@ -769,7 +772,7 @@ dont_recompile_when_opts_dont_change(Config) -> NewModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- NewFiles, filename:extension(F) == ".beam"], - ?assert(ModTime == NewModTime). + ?assertEqual(ModTime, NewModTime). dont_recompile_yrl_or_xrl(Config) -> AppDir = ?config(apps, Config), @@ -1314,6 +1317,72 @@ include_file_in_src(Config) -> ["compile"], {ok, [{app, Name}]}). +%% verify that the proper include path is defined +%% according the erlang doc which states: +%% If the filename File is absolute (possibly after variable substitution), +%% the include file with that name is included. Otherwise, the specified file +%% is searched for in the following directories, and in this order: +%% * The current working directory +%% * The directory where the module is being compiled +%% * The directories given by the include option +%% +%% This test ensures that things keep working when additional directories +%% are used for apps, such as the test/ directory within the test profile. +include_file_relative_to_working_directory_test(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + Src = <<"-module(test).\n" +"\n" +"-include(\"include/test.hrl\").\n" +"\n" +"test() -> ?TEST_MACRO.\n" +"\n">>, + Include = <<"-define(TEST_MACRO, test).\n">>, + + ok = filelib:ensure_dir(filename:join([AppDir, "src", "dummy"])), + ok = filelib:ensure_dir(filename:join([AppDir, "test", "dummy"])), + ok = file:write_file(filename:join([AppDir, "test", "test.erl"]), Src), + + ok = filelib:ensure_dir(filename:join([AppDir, "include", "dummy"])), + ok = file:write_file(filename:join([AppDir, "include", "test.hrl"]), Include), + + RebarConfig = [], + rebar_test_utils:run_and_check(Config, RebarConfig, + ["as", "test", "compile"], + {ok, [{app, Name}]}). + +%% Same as `include_file_in_src/1' but using the `test/' directory +%% within the test profile. +include_file_in_src_test(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + Src = <<"-module(test).\n" +"\n" +"-include(\"test.hrl\").\n" +"\n" +"test() -> ?TEST_MACRO.\n" +"\n">>, + Include = <<"-define(TEST_MACRO, test).\n">>, + + ok = filelib:ensure_dir(filename:join([AppDir, "src", "dummy"])), + ok = filelib:ensure_dir(filename:join([AppDir, "test", "dummy"])), + ok = file:write_file(filename:join([AppDir, "test", "test.erl"]), Src), + + ok = file:write_file(filename:join([AppDir, "src", "test.hrl"]), Include), + + RebarConfig = [], + rebar_test_utils:run_and_check(Config, RebarConfig, + ["as", "test", "compile"], + {ok, [{app, Name}]}). + always_recompile_when_erl_compiler_options_set(Config) -> %% save existing env to restore after test ExistingEnv = os:getenv("ERL_COMPILER_OPTIONS"), -- cgit v1.1 From 82e7616745bdb05143b78e1b3d3c15db709a7f0a Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 30 Sep 2016 13:28:10 -0400 Subject: Use all_src_dirs for include paths Helps cover extra cases. --- src/rebar_erlc_compiler.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index bc29939..36a247e 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -317,7 +317,7 @@ needed_files(G, ErlOpts, RebarOpts, Dir, OutDir, SourceFiles) -> TargetBase = target_base(OutDir, Source), Target = TargetBase ++ ".beam", PrivIncludes = [{i, filename:join(Dir, Src)} - || Src <- rebar_dir:src_dirs(RebarOpts, ["src"])], + || Src <- rebar_dir:all_src_dirs(RebarOpts, ["src"], [])], AllOpts = [{outdir, filename:dirname(Target)} ,{i, filename:join(Dir, "include")} ,{i, Dir}] ++ PrivIncludes ++ ErlOpts, @@ -526,7 +526,7 @@ internal_erl_compile(Opts, Dir, Module, OutDir, ErlOpts, RebarOpts) -> Target = target_base(OutDir, Module) ++ ".beam", ok = filelib:ensure_dir(Target), PrivIncludes = [{i, filename:join(Dir, Src)} - || Src <- rebar_dir:src_dirs(RebarOpts, ["src"])], + || Src <- rebar_dir:all_src_dirs(RebarOpts, ["src"], [])], AllOpts = [{outdir, filename:dirname(Target)}] ++ ErlOpts ++ PrivIncludes ++ [{i, filename:join(Dir, "include")}, {i, Dir}, return], case compile:file(Module, AllOpts) of -- cgit v1.1 From 75920a13a4251336d21bcbdbedb601dcf35de1f7 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Mon, 3 Oct 2016 23:35:58 -0400 Subject: Update existing tests to use new index structure --- test/mock_pkg_resource.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/mock_pkg_resource.erl b/test/mock_pkg_resource.erl index f837713..5769988 100644 --- a/test/mock_pkg_resource.erl +++ b/test/mock_pkg_resource.erl @@ -149,7 +149,10 @@ to_index(AllDeps, Dict) -> ets:new(package_index, [named_table, public]), dict:fold( fun(K, Deps, _) -> - DepsList = [{ec_cnv:to_binary(DK), ec_cnv:to_binary(DV)} || {DK, DV} <- Deps], + DepsList = [{DKB, {pkg, DKB, DVB, undefined}} + || {DK, DV} <- Deps, + DKB <- [ec_cnv:to_binary(DK)], + DVB <- [ec_cnv:to_binary(DV)]], ets:insert(package_index, {K, DepsList, <<"checksum">>}) end, ok, Dict), ets:insert(package_index, {package_index_version, 3}), -- cgit v1.1 From 45d9127dc952430d4b519741cb2303953e302665 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 4 Oct 2016 10:23:47 -0400 Subject: Add transitive alias tests --- test/rebar_pkg_alias_SUITE.erl | 82 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/test/rebar_pkg_alias_SUITE.erl b/test/rebar_pkg_alias_SUITE.erl index 8915357..903fdad 100644 --- a/test/rebar_pkg_alias_SUITE.erl +++ b/test/rebar_pkg_alias_SUITE.erl @@ -4,7 +4,7 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("kernel/include/file.hrl"). -all() -> [same_alias, diff_alias, diff_alias_vsn]. +all() -> [same_alias, diff_alias, diff_alias_vsn, transitive_alias]. %% {uuid, {pkg, uuid}} = uuid %% {uuid, {pkg, alias}} = uuid on disk @@ -32,6 +32,12 @@ init_per_testcase(diff_alias_vsn, Config0) -> AppDir = ?config(apps, Config), rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]), RebarConf = rebar_test_utils:create_config(AppDir, [{deps, [{fakelib, "1.0.0", {pkg, goodpkg}}]}]), + [{rebarconfig, RebarConf} | Config]; +init_per_testcase(transitive_alias, Config0) -> + Config = rebar_test_utils:init_rebar_state(Config0,"transitive_alias_vsn_"), + AppDir = ?config(apps, Config), + rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]), + RebarConf = rebar_test_utils:create_config(AppDir, [{deps, [{topdep, "1.0.0", {pkg, topdep}}]}]), [{rebarconfig, RebarConf} | Config]. end_per_testcase(_, Config) -> @@ -73,23 +79,73 @@ diff_alias(Config) -> diff_alias_vsn(Config) -> diff_alias(Config). +transitive_alias(Config) -> + %% ensure that the apps fetched under transitive aliases are + %% locked properly, but also that they are stored in the right + %% directory in the build dir to avoid breaking includes and + %% static analysis tools that rely on the location to work + AppDir = ?config(apps, Config), + Lockfile = filename:join([AppDir, "rebar.lock"]), + {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), + rebar_test_utils:run_and_check( + Config, RebarConfig, ["lock"], + {ok, [{lock, "topdep"},{dep, "topdep"}, + {lock,"transitive_app"},{dep,"transitive_app"}]} + ), + {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile), + ?assert(lists:any(fun({<<"transitive_app">>,{pkg,<<"transitive">>,_},_}) -> true + ; (_) -> false end, LockData)), + AppDir = ?config(apps, Config), + AliasedName = filename:join([AppDir, "_build", "default", "lib", "transitive_app"]), + PkgName = filename:join([AppDir, "_build", "default", "lib", "transitive"]), + ?assert(filelib:is_dir(AliasedName)), + ?assertNot(filelib:is_dir(PkgName)), + %% An second run yields the same + rebar_test_utils:run_and_check( + Config, RebarConfig, ["lock"], + {ok, [{lock, "topdep"},{dep, "topdep"}, + {lock,"transitive_app"},{dep,"transitive_app"}]} + ), + {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile), + ?assert(filelib:is_dir(AliasedName)), + ?assertNot(filelib:is_dir(PkgName)), + %% So does an upgrade + rebar_test_utils:run_and_check( + Config, RebarConfig, ["upgrade"], + {ok, [{lock, "topdep"},{dep, "topdep"}, + {lock,"transitive_app"},{dep,"transitive_app"}]} + ), + {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile), + ?assert(filelib:is_dir(AliasedName)), + ?assertNot(filelib:is_dir(PkgName)), + ok. + mock_config(Name, Config) -> + {ChkFake, Etag} = create_lib(Name, Config, "fakelib"), + {ChkTop, _} = create_lib(Name, Config, "topdep"), + {ChkTrans, _} = create_lib(Name, Config, "transitive_app", "transitive"), Priv = ?config(priv_dir, Config), + TmpDir = filename:join([Priv, "tmp", atom_to_list(Name)]), + %% Add an alias for goodpkg -> fakelib by hand AppDir = filename:join([Priv, "fakelib"]), CacheRoot = filename:join([Priv, "cache", atom_to_list(Name)]), - TmpDir = filename:join([Priv, "tmp", atom_to_list(Name)]), CacheDir = filename:join([CacheRoot, "hex", "com", "test", "packages"]), - filelib:ensure_dir(filename:join([CacheDir, "registry"])), rebar_test_utils:create_app(AppDir, "fakelib", "1.0.0", [kernel, stdlib]), - {Chk,Etag} = rebar_test_utils:package_app(AppDir, CacheDir, "fakelib-1.0.0"), - {Chk,Etag} = rebar_test_utils:package_app(AppDir, CacheDir, "goodpkg-1.0.0"), + {ChkFake, Etag} = rebar_test_utils:package_app(AppDir, CacheDir, "goodpkg-1.0.0"), Tid = ets:new(registry_table, [public]), ets:insert_new(Tid, [ {<<"fakelib">>,[[<<"1.0.0">>]]}, {<<"goodpkg">>,[[<<"1.0.0">>]]}, - {{<<"fakelib">>,<<"1.0.0">>}, [[], Chk, [<<"rebar3">>]]}, - {{<<"goodpkg">>,<<"1.0.0">>}, [[], Chk, [<<"rebar3">>]]} + {<<"topdep">>,[[<<"1.0.0">>]]}, + {<<"transitive">>, [[<<"1.0.0">>]]}, + {{<<"fakelib">>,<<"1.0.0">>}, [[], ChkFake, [<<"rebar3">>]]}, + {{<<"goodpkg">>,<<"1.0.0">>}, [[], ChkFake, [<<"rebar3">>]]}, + {{<<"topdep">>,<<"1.0.0">>}, + [[ + [<<"transitive">>, <<"1.0.0">>, false, <<"transitive_app">>] + ], ChkTop, [<<"rebar3">>]]}, + {{<<"transitive">>,<<"1.0.0">>}, [[], ChkTrans, [<<"rebar3">>]]} ]), ok = ets:tab2file(Tid, filename:join([CacheDir, "registry"])), ets:delete(Tid), @@ -119,3 +175,15 @@ mock_config(Name, Config) -> unmock_config(Config) -> meck:unload(), Config. + +create_lib(Name, Config, PkgName) -> + create_lib(Name, Config, PkgName, PkgName). + +create_lib(Name, Config, AppName, PkgName) -> + Priv = ?config(priv_dir, Config), + AppDir = filename:join([Priv, PkgName]), + CacheRoot = filename:join([Priv, "cache", atom_to_list(Name)]), + CacheDir = filename:join([CacheRoot, "hex", "com", "test", "packages"]), + filelib:ensure_dir(filename:join([CacheDir, "registry"])), + rebar_test_utils:create_app(AppDir, AppName, "1.0.0", [kernel, stdlib]), + rebar_test_utils:package_app(AppDir, CacheDir, PkgName++"-1.0.0"). -- cgit v1.1 From 1f86ed1aed990438103c5f668c4ec930ab637fc9 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Tue, 4 Oct 2016 22:34:22 -0400 Subject: Track package hash in memory index, add hash test This adds tracking of package hash in the in-memory index rather than the current `undefined' values. According to the test added, this is not necessary for transitive package dep hash chcking, but does result in a more complete index search result when doing app lookups, and could yield some optimizations on hash checks by checking from the index structure before fetching a package. --- src/rebar_prv_update.erl | 18 ++++++++++++++++- test/rebar_pkg_alias_SUITE.erl | 44 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index ff7194a..75c609e 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -104,7 +104,8 @@ hex_to_index(State) -> case lists:any(fun is_supported/1, BuildTools) of true -> DepsList = update_deps_list(Pkg, PkgVsn, Deps, Registry, State), - ets:insert(?PACKAGE_TABLE, {{Pkg, PkgVsn}, DepsList, Checksum}); + HashedDeps = update_deps_hashes(DepsList), + ets:insert(?PACKAGE_TABLE, {{Pkg, PkgVsn}, HashedDeps, Checksum}); false -> true end; @@ -176,6 +177,21 @@ update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) -> DepsListAcc end, [], Deps). +update_deps_hashes(List) -> + [{Name, {pkg, PkgName, Vsn, lookup_hash(PkgName, Vsn, Hash)}} + || {Name, {pkg, PkgName, Vsn, Hash}} <- List]. + +lookup_hash(Name, Vsn, undefined) -> + try + ets:lookup_element(?PACKAGE_TABLE, {Name, Vsn}, 3) + catch + _:_ -> + undefined + end; +lookup_hash(_, _, Hash) -> + Hash. + + rm_ws(<<" ", R/binary>>) -> rm_ws(R); rm_ws(R) -> diff --git a/test/rebar_pkg_alias_SUITE.erl b/test/rebar_pkg_alias_SUITE.erl index 903fdad..2b8ccd2 100644 --- a/test/rebar_pkg_alias_SUITE.erl +++ b/test/rebar_pkg_alias_SUITE.erl @@ -4,7 +4,8 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("kernel/include/file.hrl"). -all() -> [same_alias, diff_alias, diff_alias_vsn, transitive_alias]. +all() -> [same_alias, diff_alias, diff_alias_vsn, transitive_alias, + transitive_hash_mismatch]. %% {uuid, {pkg, uuid}} = uuid %% {uuid, {pkg, alias}} = uuid on disk @@ -38,6 +39,12 @@ init_per_testcase(transitive_alias, Config0) -> AppDir = ?config(apps, Config), rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]), RebarConf = rebar_test_utils:create_config(AppDir, [{deps, [{topdep, "1.0.0", {pkg, topdep}}]}]), + [{rebarconfig, RebarConf} | Config]; +init_per_testcase(transitive_hash_mismatch, Config0) -> + Config = rebar_test_utils:init_rebar_state(Config0,"transitive_alias_vsn_"), + AppDir = ?config(apps, Config), + rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]), + RebarConf = rebar_test_utils:create_config(AppDir, [{deps, [{topdep, "1.0.0", {pkg, topdep}}]}]), [{rebarconfig, RebarConf} | Config]. end_per_testcase(_, Config) -> @@ -120,6 +127,41 @@ transitive_alias(Config) -> ?assertNot(filelib:is_dir(PkgName)), ok. +transitive_hash_mismatch(Config) -> + %% ensure that the apps fetched under transitive aliases are + %% locked properly, but also that they are stored in the right + %% directory in the build dir to avoid breaking includes and + %% static analysis tools that rely on the location to work + AppDir = ?config(apps, Config), + Lockfile = filename:join([AppDir, "rebar.lock"]), + {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), + rebar_test_utils:run_and_check( + Config, RebarConfig, ["lock"], + {ok, [{lock, "topdep"},{dep, "topdep"}, + {lock,"transitive_app"},{dep,"transitive_app"}]} + ), + {ok, [LockData|Attrs]} = file:consult(Lockfile), + %% Change Lock hash data to cause a failure next time, but on transitive + %% deps only + NewLock = [LockData|lists:map( + fun([{pkg_hash, Hashes}|Rest]) -> + [{pkg_hash, [{<<"transitive_app">>, <<"fakehash">>} + | lists:keydelete(<<"transitive_app">>, 1, Hashes)]} + | Rest] + ; (Attr) -> + Attr + end, Attrs)], + {ok, Io} = file:open(Lockfile, [write]), + [io:format(Io, "~p.~n", [Attr]) || Attr <- NewLock], + file:close(Io), + ct:pal("lock: ~p", [file:consult(Lockfile)]), + ec_file:remove(filename:join([AppDir, "_build"]), [recursive]), + ?assertMatch( + {error, {rebar_fetch, {unexpected_hash, _, _, _}}}, + rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], return) + ), + ok. + mock_config(Name, Config) -> {ChkFake, Etag} = create_lib(Name, Config, "fakelib"), {ChkTop, _} = create_lib(Name, Config, "topdep"), -- cgit v1.1 From f824f92ce980bd854a76ebc5b3cbcf6813432f7c Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Wed, 5 Oct 2016 14:13:11 +0100 Subject: Update Travis CI to latest OTP19.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dbb4f26..5e17e50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false language: erlang install: 'true' otp_release: -- 19.0 +- 19.1 - 18.0 - 17.5 - R16B03-1 -- cgit v1.1 From 3966610e532f18f71c6e4d04c3a96d6372ddc390 Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Tue, 11 Oct 2016 10:31:57 +0300 Subject: Made Common Test load the user's applications before slurping config. --- src/rebar_prv_common_test.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 1e0632e..8bfa192 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -221,6 +221,7 @@ select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> Configs = lists:flatmap(fun(Filename) -> rebar_file_utils:consult_config(State, Filename) end, SysConfigs), + code:add_pathsa(rebar_state:code_paths(State, all_deps)), [application:load(Application) || Config <- Configs, {Application, _} <- Config], rebar_utils:reread_config(Configs), -- cgit v1.1 From 6e9503c4f0232d6e8152b56a7037f22843e53959 Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Tue, 11 Oct 2016 10:33:14 +0300 Subject: Rereading system configuration sets up persistent options if possible. --- src/rebar_utils.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index aa9e268..dc0bb42 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -414,8 +414,14 @@ user_agent() -> ?FMT("Rebar/~s (OTP/~s)", [Vsn, otp_release()]). reread_config(ConfigList) -> + SetEnv = case version_tuple(?MODULE:otp_release()) of + {X, _, _} when X =< 17 -> + fun application:set_env/3; + _ -> + fun (App, Key, Val) -> application:set_env(App, Key, Val, [{persistent, true}]) end + end, try - [application:set_env(Application, Key, Val) + [SetEnv(Application, Key, Val) || Config <- ConfigList, {Application, Items} <- Config, {Key, Val} <- Items] -- cgit v1.1 From 0afb4c4d477b1c1fba54ad3ef086d0697a5faeaa Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Tue, 11 Oct 2016 10:36:12 +0300 Subject: Made reading sys.configs consistent with OTP specification. --- src/rebar_file_utils.erl | 13 ++++++++----- src/rebar_prv_common_test.erl | 6 +++--- src/rebar_prv_shell.erl | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 437780d..28cc516 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -72,11 +72,14 @@ consult_config(State, Filename) -> [T] -> T; [] -> [] end, - SubConfigs = [consult_config(State, Entry ++ ".config") || - Entry <- Config, is_list(Entry) - ], - - [Config | lists:merge(SubConfigs)]. + lists:flatmap( + fun (SubConfig) when is_list(SubConfig) -> + case consult_config(State, SubConfig) of + [] -> consult_config(State, SubConfig ++ ".config"); + X -> X + end; + (Entry) -> [Entry] + end, Config). format_error({bad_term_file, AppFile, Reason}) -> io_lib:format("Error reading file ~s: ~s", [AppFile, file:format_error(Reason)]). diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 8bfa192..e69d33e 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -218,9 +218,9 @@ select_tests(_, _, _, {error, _} = Error) -> Error; select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> %% set application env if sys_config argument is provided SysConfigs = sys_config_list(CmdOpts, CfgOpts), - Configs = lists:flatmap(fun(Filename) -> - rebar_file_utils:consult_config(State, Filename) - end, SysConfigs), + Configs = lists:map(fun(Filename) -> + rebar_file_utils:consult_config(State, Filename) + end, SysConfigs), code:add_pathsa(rebar_state:code_paths(State, all_deps)), [application:load(Application) || Config <- Configs, {Application, _} <- Config], rebar_utils:reread_config(Configs), diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index b7febf8..e14ae3a 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -331,8 +331,8 @@ reread_config(State) -> case find_config(State) of no_config -> ok; - ConfigList -> - _ = rebar_utils:reread_config(ConfigList), + Config -> + _ = rebar_utils:reread_config([Config]), ok end. -- cgit v1.1 From 4ce21e4c800108030d5c646b226032f460e68065 Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Tue, 11 Oct 2016 16:30:27 +0300 Subject: Avoid backward-compatibility-breaking changes. --- src/rebar_file_utils.erl | 18 ++++++++++-------- src/rebar_prv_common_test.erl | 6 +++--- src/rebar_prv_shell.erl | 4 ++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 28cc516..6721b5a 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -72,14 +72,16 @@ consult_config(State, Filename) -> [T] -> T; [] -> [] end, - lists:flatmap( - fun (SubConfig) when is_list(SubConfig) -> - case consult_config(State, SubConfig) of - [] -> consult_config(State, SubConfig ++ ".config"); - X -> X - end; - (Entry) -> [Entry] - end, Config). + JoinedConfig = lists:flatmap( + fun (SubConfig) when is_list(SubConfig) -> + case lists:suffix(".config", SubConfig) of + false -> consult_config(State, SubConfig ++ ".config"); + true -> consult_config(State, SubConfig) + end; + (Entry) -> [Entry] + end, Config), + %% Backwards compatibility + [JoinedConfig]. format_error({bad_term_file, AppFile, Reason}) -> io_lib:format("Error reading file ~s: ~s", [AppFile, file:format_error(Reason)]). diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index e69d33e..8bfa192 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -218,9 +218,9 @@ select_tests(_, _, _, {error, _} = Error) -> Error; select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> %% set application env if sys_config argument is provided SysConfigs = sys_config_list(CmdOpts, CfgOpts), - Configs = lists:map(fun(Filename) -> - rebar_file_utils:consult_config(State, Filename) - end, SysConfigs), + Configs = lists:flatmap(fun(Filename) -> + rebar_file_utils:consult_config(State, Filename) + end, SysConfigs), code:add_pathsa(rebar_state:code_paths(State, all_deps)), [application:load(Application) || Config <- Configs, {Application, _} <- Config], rebar_utils:reread_config(Configs), diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index e14ae3a..b7febf8 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -331,8 +331,8 @@ reread_config(State) -> case find_config(State) of no_config -> ok; - Config -> - _ = rebar_utils:reread_config([Config]), + ConfigList -> + _ = rebar_utils:reread_config(ConfigList), ok end. -- cgit v1.1 From e71b80752f905d9f4bb5dbf78c16693dbc6133f4 Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Tue, 11 Oct 2016 16:34:21 +0300 Subject: Some post-review changes: - restore path after loading applications, - helpful comments. --- src/rebar_prv_common_test.erl | 4 ++++ src/rebar_utils.erl | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 8bfa192..46bd1a7 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -221,9 +221,13 @@ select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> Configs = lists:flatmap(fun(Filename) -> rebar_file_utils:consult_config(State, Filename) end, SysConfigs), + %% NB: load the applications (from user directories too) to support OTP < 17 + %% to our best ability. + OldPath = code:get_path(), code:add_pathsa(rebar_state:code_paths(State, all_deps)), [application:load(Application) || Config <- Configs, {Application, _} <- Config], rebar_utils:reread_config(Configs), + code:set_path(OldPath), Merged = lists:ukeymerge(1, lists:ukeysort(1, CmdOpts), diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index dc0bb42..f55f40f 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -414,6 +414,8 @@ user_agent() -> ?FMT("Rebar/~s (OTP/~s)", [Vsn, otp_release()]). reread_config(ConfigList) -> + %% NB: we attempt to mimic -config here, which survives app reload, + %% hence {persistent, true}. SetEnv = case version_tuple(?MODULE:otp_release()) of {X, _, _} when X =< 17 -> fun application:set_env/3; -- cgit v1.1 From 294fd72f4d8f3beb2e856f085e634549d978b7fd Mon Sep 17 00:00:00 2001 From: Alexander Sedov Date: Thu, 13 Oct 2016 12:46:18 +0300 Subject: Get stacktrace only once and as soon as possible. Fixes #1347. It turns out that pretty-printing uses throw-catch and thus garbages out the stack trace sometimes, so we should get it only once. --- src/rebar3.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rebar3.erl b/src/rebar3.erl index c665f20..47dc25a 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -286,10 +286,11 @@ handle_error({error, Error}) when is_list(Error) -> handle_error(Error) -> %% Nothing should percolate up from rebar_core; %% Dump this error to console - ?CRASHDUMP("Error: ~p~n~p~n~n", [Error, erlang:get_stacktrace()]), + StackTrace = erlang:get_stacktrace(), + ?CRASHDUMP("Error: ~p~n~p~n~n", [Error, StackTrace]), ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace or consult rebar3.crashdump", []), ?DEBUG("Uncaught error: ~p", [Error]), - case erlang:get_stacktrace() of + case StackTrace of [] -> ok; Trace -> ?DEBUG("Stack trace to the error location:~n~p", [Trace]) -- cgit v1.1 From 56c943a1b8241c2ff165104a821b68d7298ad7a4 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 14 Oct 2016 07:38:48 -0700 Subject: upgrade relx to 3.21.1 --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 0344032..f61d3e5 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.3.0"}, - {relx, "3.21.0"}, + {relx, "3.21.1"}, {cf, "0.2.1"}, {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. diff --git a/rebar.lock b/rebar.lock index 025c9fb..8f0f659 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,7 +7,7 @@ {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.21.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.21.1">>},0}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ @@ -19,6 +19,6 @@ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"91E1EA9F09B4EDFDA8461901F4B5C5E0226E43EC161E147EEAB29F7761DF6EB5">>}, + {<<"relx">>, <<"F989DC520730EFD9075E9F4DEBCB8BA1D7D1E86B018B0BCF45A2EB80270B4AD6">>}, {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} ]. -- cgit v1.1 From f0d1a8404c9488135a120e4640c76ff3ff1eda66 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 14 Oct 2016 19:12:43 -0400 Subject: Bump version to 3.3.2 --- bootstrap | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index 2c9c66f..c36fddb 100755 --- a/bootstrap +++ b/bootstrap @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.3.1", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.3.2", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence diff --git a/src/rebar.app.src b/src/rebar.app.src index 5b735cf..bf520aa 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.3.2"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 94f87477d5db0243992ca222c79486acbfb9fcee Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 14 Oct 2016 19:13:38 -0400 Subject: Return to git-based vsn --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index bf520aa..5b735cf 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.3.2"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 504431473b9cc7f9c1641f640fc4d8a02c9aa079 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Mon, 17 Oct 2016 08:41:25 -0400 Subject: Prevent crashes in `rebar3 as` with no tasks checks on hd(...) and so on could not handle empty lists --- src/rebar_prv_as.erl | 6 ++++-- test/rebar_as_SUITE.erl | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/rebar_prv_as.erl b/src/rebar_prv_as.erl index b4f7ac4..e7c6d68 100644 --- a/src/rebar_prv_as.erl +++ b/src/rebar_prv_as.erl @@ -33,9 +33,11 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> {Profiles, Tasks} = args_to_profiles_and_tasks(rebar_state:command_args(State)), - case Profiles of - [] -> + case {Profiles, Tasks} of + {[], _} -> {error, "At least one profile must be specified when using `as`"}; + {_, []} -> + {error, "At least one task must be specified when using `as`"}; _ -> warn_on_empty_profile(Profiles, State), State1 = rebar_state:apply_profiles(State, [list_to_atom(X) || X <- Profiles]), diff --git a/test/rebar_as_SUITE.erl b/test/rebar_as_SUITE.erl index 0f37dc8..ce8046b 100644 --- a/test/rebar_as_SUITE.erl +++ b/test/rebar_as_SUITE.erl @@ -14,6 +14,7 @@ as_dir_name/1, as_with_task_args/1, warn_on_empty_profile/1, + error_on_empty_tasks/1, clean_as_profile/1]). -include_lib("common_test/include/ct.hrl"). @@ -33,7 +34,7 @@ all() -> [as_basic, as_multiple_profiles, as_multiple_tasks, as_multiple_profiles_multiple_tasks, as_comma_placement, as_comma_then_space, as_dir_name, as_with_task_args, - warn_on_empty_profile, clean_as_profile]. + warn_on_empty_profile, error_on_empty_tasks, clean_as_profile]. as_basic(Config) -> AppDir = ?config(apps, Config), @@ -159,6 +160,20 @@ warn_on_empty_profile(Config) -> meck:unload(rebar_log), ok. +error_on_empty_tasks(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("as_error_empty_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + meck:new(rebar_log, [passthrough]), + rebar_test_utils:run_and_check(Config, + [], + ["as", "default"], + {error, "At least one task must be specified when using `as`"}), + ok. + warn_match(App, History) -> lists:any( fun({_, {rebar_log,log, [warn, "No entry for profile ~s in config.", -- cgit v1.1 From 203e5c15bd1935d66ca29fb071a8bc7ba8dad162 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Mon, 17 Oct 2016 17:10:41 -0700 Subject: allow test specifications to be passed via the command line `rebar3 ct --spec foo.spec,bar.spec,baz.spec` now works also added support for the `join_specs` flag on the command line --- src/rebar_prv_common_test.erl | 11 ++++++--- test/rebar_ct_SUITE.erl | 54 ++++++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 46bd1a7..189a26f 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -139,6 +139,8 @@ transform_opts([{testcase, Cases}|Rest], Acc) -> transform_opts(Rest, [{testcase, split_string(Cases)}|Acc]); transform_opts([{config, Configs}|Rest], Acc) -> transform_opts(Rest, [{config, split_string(Configs)}|Acc]); +transform_opts([{spec, Specs}|Rest], Acc) -> + transform_opts(Rest, [{spec, split_string(Specs)}|Acc]); transform_opts([{include, Includes}|Rest], Acc) -> transform_opts(Rest, [{include, split_string(Includes)}|Acc]); transform_opts([{logopts, LogOpts}|Rest], Acc) -> @@ -174,9 +176,6 @@ cfgopts(State) -> end. ensure_opts([], Acc) -> lists:reverse(Acc); -ensure_opts([{test_spec, _}|Rest], Acc) -> - ?WARN("Test specs not supported. See http://www.rebar3.org/docs/running-tests#common-test", []), - ensure_opts(Rest, Acc); ensure_opts([{cover, _}|Rest], Acc) -> ?WARN("Cover specs not supported. See http://www.rebar3.org/docs/running-tests#common-test", []), ensure_opts(Rest, Acc); @@ -650,6 +649,8 @@ ct_opts(_State) -> {testcase, undefined, "case", string, help(testcase)}, %% comma-seperated list {label, undefined, "label", string, help(label)}, %% String {config, undefined, "config", string, help(config)}, %% comma-seperated list + {spec, undefined, "spec", string, help(spec)}, %% common-seperated list + {join_specs, undefined, "join_specs", boolean, help(join_specs)}, {allow_user_terms, undefined, "allow_user_terms", boolean, help(allow_user_terms)}, %% Bool {logdir, undefined, "logdir", string, help(logdir)}, %% dir {logopts, undefined, "logopts", string, help(logopts)}, %% comma seperated list @@ -688,6 +689,10 @@ help(label) -> "Test label"; help(config) -> "List of config files"; +help(spec) -> + "List of test specifications"; +help(join_specs) -> + "Merge all test specifications and perform a single test run"; help(sys_config) -> "List of application config files"; help(allow_user_terms) -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index c10875b..8e989b5 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -24,6 +24,8 @@ data_dir_correct/1, cmd_label/1, cmd_config/1, + cmd_spec/1, + cmd_join_specs/1, cmd_allow_user_terms/1, cmd_logdir/1, cmd_logopts/1, @@ -44,7 +46,6 @@ cmd_sys_config/1, cfg_opts/1, cfg_arbitrary_opts/1, - cfg_test_spec/1, cfg_cover_spec/1, cfg_atom_suites/1, cover_compiled/1, @@ -62,7 +63,7 @@ all() -> [{group, basic_app}, {group, ct_opts}, {group, cover}, cfg_opts, cfg_arbitrary_opts, - cfg_test_spec, cfg_cover_spec, + cfg_cover_spec, cfg_atom_suites, misspecified_ct_opts, misspecified_ct_compile_opts, @@ -88,6 +89,8 @@ groups() -> [{basic_app, [], [basic_app_default_dirs, {data_dirs, [], [data_dir_correct]}, {ct_opts, [], [cmd_label, cmd_config, + cmd_spec, + cmd_join_specs, cmd_allow_user_terms, cmd_logdir, cmd_logopts, @@ -761,6 +764,36 @@ cmd_config(Config) -> true = lists:member({config, ["config/foo", "config/bar", "config/baz"]}, TestOpts). +cmd_spec(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--spec=foo.spec,bar.spec,baz.spec"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({spec, ["foo.spec", "bar.spec", "baz.spec"]}, TestOpts). + +cmd_join_specs(Config) -> + State = ?config(result, Config), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--join_specs=true"]), + + NewState = rebar_state:command_parsed_args(State, GetOptResult), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(NewState), + + true = lists:member({join_specs, true}, TestOpts). + cmd_allow_user_terms(Config) -> State = ?config(result, Config), @@ -1096,23 +1129,6 @@ cfg_arbitrary_opts(Config) -> true = lists:member({bar, 2}, TestOpts), true = lists:member({baz, 3}, TestOpts). -cfg_test_spec(Config) -> - C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_test_spec_opts_"), - - AppDir = ?config(apps, C), - - Name = rebar_test_utils:create_random_name("ct_cfg_test_spec_opts_"), - Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), - - RebarConfig = [{ct_opts, [Opt = {test_spec, "spec/foo.spec"}]}], - - {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), - - {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), - - false = lists:member(Opt, TestOpts). - cfg_cover_spec(Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_cover_spec_opts_"), -- cgit v1.1 From 14956eaf168dd20b0b1da1c05a82d66da77ec37c Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Mon, 17 Oct 2016 18:52:09 -0700 Subject: fix "helpful" compiler spelling correction --- src/rebar_prv_common_test.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 189a26f..b91c90f 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -649,7 +649,7 @@ ct_opts(_State) -> {testcase, undefined, "case", string, help(testcase)}, %% comma-seperated list {label, undefined, "label", string, help(label)}, %% String {config, undefined, "config", string, help(config)}, %% comma-seperated list - {spec, undefined, "spec", string, help(spec)}, %% common-seperated list + {spec, undefined, "spec", string, help(spec)}, %% comma-seperated list {join_specs, undefined, "join_specs", boolean, help(join_specs)}, {allow_user_terms, undefined, "allow_user_terms", boolean, help(allow_user_terms)}, %% Bool {logdir, undefined, "logdir", string, help(logdir)}, %% dir -- cgit v1.1 From bcfd8d6f80f551e1b2a0e23eaa6b5ff2c7da5b15 Mon Sep 17 00:00:00 2001 From: James Fish Date: Thu, 3 Nov 2016 15:45:41 +0000 Subject: Add exclude_apps/mods, plt_extra_mods, base_plt_mods config * exclude_apps - never use applications for PLT/analysis * base_plt_mods - add modules to base PLT (overrules exclude_apps) * plt_extra_mods - add modules to PLT (overrules exclude_apps) * exclude_mods - never use modules for PLT/analysis (overrules all) --- src/rebar_prv_dialyzer.erl | 162 +++++++++++++++++++++++++++--------------- test/rebar_dialyzer_SUITE.erl | 38 +++++++++- 2 files changed, 141 insertions(+), 59 deletions(-) diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index fc13de1..44dc0d2 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -47,26 +47,33 @@ desc() -> "`plt_apps` - the strategy for determining the applications which included " "in the PLT file, `top_level_deps` to include just the direct dependencies " "or `all_deps` to include all nested dependencies*\n" - "`plt_extra_apps` - a list of applications to include in the PLT file**\n" + "`plt_extra_apps` - a list of extra applications to include in the PLT " + "file\n" + "`plt_extra_mods` - a list of extra modules to includes in the PLT file\n" "`plt_location` - the location of the PLT file, `local` to store in the " "profile's base directory (default) or a custom directory.\n" - "`plt_prefix` - the prefix to the PLT file, defaults to \"rebar3\"***\n" + "`plt_prefix` - the prefix to the PLT file, defaults to \"rebar3\"**\n" "`base_plt_apps` - a list of applications to include in the base " - "PLT file****\n" + "PLT file***\n" + "`base_plt_mods` - a list of modules to include in the base " + "PLT file***\n" "`base_plt_location` - the location of base PLT file, `global` to store in " - "$HOME/.cache/rebar3 (default) or a custom directory****\n" + "$HOME/.cache/rebar3 (default) or a custom directory***\n" "`base_plt_prefix` - the prefix to the base PLT file, defaults to " - "\"rebar3\"*** ****\n" + "\"rebar3\"** ***\n" + "`exclude_apps` - a list of applications to exclude from PLT files and " + "success typing analysis, `plt_extra_mods` and `base_plt_mods` can add " + "modules from excluded applications\n" + "`exclude_mods` - a list of modules to exclude from PLT files and " + "success typing analysis\n" "\n" "For example, to warn on unmatched returns: \n" "{dialyzer, [{warnings, [unmatched_returns]}]}.\n" "\n" "*The direct dependent applications are listed in `applications` and " "`included_applications` of their .app files.\n" - "**The applications in `base_plt_apps` will be added to the " - "list. \n" - "***PLT files are named \"__plt\".\n" - "****The base PLT is a PLT containing the core applications often required " + "**PLT files are named \"__plt\".\n" + "***The base PLT is a PLT containing the core applications often required " "for a project's PLT. One base PLT is created per OTP version and " "stored in `base_plt_location`. A base PLT is used to build project PLTs." "\n". @@ -90,6 +97,10 @@ do(State) -> ?PRV_ERROR({dialyzer_warnings, Warnings}); throw:{unknown_application, _} = Error -> ?PRV_ERROR(Error); + throw:{unknown_module, _} = Error -> + ?PRV_ERROR(Error); + throw:{duplicate_module, _, _, _} = Error -> + ?PRV_ERROR(Error); throw:{output_file_error, _, _} = Error -> ?PRV_ERROR(Error) after @@ -110,6 +121,10 @@ format_error({dialyzer_warnings, Warnings}) -> io_lib:format("Warnings occured running dialyzer: ~b", [Warnings]); format_error({unknown_application, App}) -> io_lib:format("Could not find application: ~s", [App]); +format_error({unknown_module, Mod}) -> + io_lib:format("Could not find module: ~s", [Mod]); +format_error({duplicate_module, Mod, File1, File2}) -> + io_lib:format("Duplicates of module ~s: ~s ~s", [Mod, File1, File2]); format_error({output_file_error, File, Error}) -> Error1 = file:format_error(Error), io_lib:format("Failed to write to ~s: ~s", [File, Error1]); @@ -178,45 +193,45 @@ do_update_proj_plt(State, Plt, Output) -> end. proj_plt_files(State) -> - BasePltApps = get_config(State, base_plt_apps, default_plt_apps()), - PltApps = get_config(State, plt_extra_apps, []), + BasePltApps = base_plt_apps(State), + PltApps = get_config(State, plt_extra_apps, []) ++ BasePltApps, + BasePltMods = get_config(State, base_plt_mods, []), + PltMods = get_config(State, plt_extra_mods, []) ++ BasePltMods, + Apps = proj_apps(State), + DepApps = proj_deps(State), + get_files(State, DepApps ++ PltApps, Apps -- PltApps, PltMods, []). + +proj_apps(State) -> + [ec_cnv:to_atom(rebar_app_info:name(App)) || + App <- rebar_state:project_apps(State)]. + +proj_deps(State) -> Apps = rebar_state:project_apps(State), DepApps = lists:flatmap(fun rebar_app_info:applications/1, Apps), - DepApps1 = - case get_config(State, plt_apps, top_level_deps) of - top_level_deps -> DepApps; - all_deps -> collect_nested_dependent_apps(DepApps) - end, - get_plt_files(BasePltApps ++ PltApps ++ DepApps1, Apps). - -default_plt_apps() -> - [erts, - crypto, - kernel, - stdlib]. - -get_plt_files(DepApps, Apps) -> + case get_config(State, plt_apps, top_level_deps) of + top_level_deps -> DepApps; + all_deps -> collect_nested_dependent_apps(DepApps) + end. + +get_files(State, Apps, SkipApps, Mods, SkipMods) -> ?INFO("Resolving files...", []), - get_plt_files(DepApps, Apps, [], []). + ExcludeApps = get_config(State, exclude_apps, []), + Files = apps_files(Apps, ExcludeApps ++ SkipApps, dict:new()), + ExcludeMods = get_config(State, exclude_mods, []), + Files2 = mods_files(Mods, ExcludeMods ++ SkipMods, Files), + dict:fold(fun(_, File, Acc) -> [File | Acc] end, [], Files2). -get_plt_files([], _, _, Files) -> +apps_files([], _, Files) -> Files; -get_plt_files([AppName | DepApps], Apps, PltApps, Files) -> - case lists:member(AppName, PltApps) orelse app_member(AppName, Apps) of +apps_files([AppName | DepApps], SkipApps, Files) -> + case lists:member(AppName, SkipApps) of true -> - get_plt_files(DepApps, Apps, PltApps, Files); + apps_files(DepApps, SkipApps, Files); false -> - Files2 = app_files(AppName), - ?DEBUG("~s files: ~p", [AppName, Files2]), - get_plt_files(DepApps, Apps, [AppName | PltApps], Files2 ++ Files) - end. - -app_member(AppName, Apps) -> - case rebar_app_utils:find(ec_cnv:to_binary(AppName), Apps) of - {ok, _App} -> - true; - error -> - false + AppFiles = app_files(AppName), + ?DEBUG("~s modules: ~p", [AppName, dict:fetch_keys(AppFiles)]), + Files2 = merge_files(Files, AppFiles), + apps_files(DepApps, [AppName | SkipApps], Files2) end. app_files(AppName) -> @@ -244,9 +259,41 @@ check_ebin(EbinDir) -> end. ebin_files(EbinDir) -> - Wildcard = "*" ++ code:objfile_extension(), - [filename:join(EbinDir, File) || - File <- filelib:wildcard(Wildcard, EbinDir)]. + Ext = code:objfile_extension(), + Wildcard = "*" ++ Ext, + Files = filelib:wildcard(Wildcard, EbinDir), + Store = fun(File, Mods) -> + Mod = list_to_atom(filename:basename(File, Ext)), + Absname = filename:join(EbinDir, File), + dict:store(Mod, Absname, Mods) + end, + lists:foldl(Store, dict:new(), Files). + +merge_files(Files1, Files2) -> + Duplicate = fun(Mod, File1, File2) -> + throw({duplicate_module, Mod, File1, File2}) + end, + dict:merge(Duplicate, Files1, Files2). + +mods_files(Mods, SkipMods, Files) -> + Keep = fun(File) -> File end, + Ensure = fun(Mod, Acc) -> + case lists:member(Mod, SkipMods) of + true -> + Acc; + false -> + dict:update(Mod, Keep, mod_file(Mod), Acc) + end + end, + Files2 = lists:foldl(Ensure, Files, Mods), + lists:foldl(fun dict:erase/2, Files2, SkipMods). + +mod_file(Mod) -> + File = atom_to_list(Mod) ++ code:objfile_extension(), + case code:where_is_file(File) of + non_existing -> throw({unknown_module, Mod}); + Absname -> Absname + end. read_plt(_State, Plt) -> Vsn = dialyzer_version(), @@ -355,9 +402,12 @@ get_base_plt(State) -> end. base_plt_files(State) -> - BasePltApps = get_config(State, base_plt_apps, default_plt_apps()), - Apps = rebar_state:project_apps(State), - get_plt_files(BasePltApps, Apps). + BasePltApps = base_plt_apps(State), + BasePltMods = get_config(State, base_plt_mods, []), + get_files(State, BasePltApps, [], BasePltMods, []). + +base_plt_apps(State) -> + get_config(State, base_plt_apps, [erts, crypto, kernel, stdlib]). update_base_plt(State, BasePlt, Output, BaseFiles) -> case read_plt(State, BasePlt) of @@ -394,9 +444,8 @@ succ_typings(State, Plt, Output) -> false -> {0, State}; _ -> - Apps = rebar_state:project_apps(State), ?INFO("Doing success typing analysis...", []), - Files = apps_to_files(Apps), + Files = proj_files(State), succ_typings(State, Plt, Output, Files) end. @@ -412,14 +461,13 @@ succ_typings(State, Plt, Output, Files) -> {init_plt, Plt}], run_dialyzer(State, Opts, Output). -apps_to_files(Apps) -> - ?INFO("Resolving files...", []), - [File || App <- Apps, - File <- app_to_files(App)]. - -app_to_files(App) -> - AppName = ec_cnv:to_atom(rebar_app_info:name(App)), - app_files(AppName). +proj_files(State) -> + Apps = proj_apps(State), + BasePltApps = get_config(State, base_plt_apps, []), + PltApps = get_config(State, plt_extra_apps, []) ++ BasePltApps, + BasePltMods = get_config(State, base_plt_mods, []), + PltMods = get_config(State, plt_extra_mods, []) ++ BasePltMods, + get_files(State, Apps, PltApps, [], PltMods). run_dialyzer(State, Opts, Output) -> %% dialyzer may return callgraph warnings when get_warnings is false diff --git a/test/rebar_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl index e5d8c52..d0a3611 100644 --- a/test/rebar_dialyzer_SUITE.erl +++ b/test/rebar_dialyzer_SUITE.erl @@ -14,7 +14,8 @@ update_base_plt/1, update_app_plt/1, build_release_plt/1, - plt_apps_option/1]). + plt_apps_option/1, + exclude_and_extra/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -57,7 +58,7 @@ all() -> groups() -> [{empty, [empty_base_plt, empty_app_plt, empty_app_succ_typings]}, - {build_and_check, [build_release_plt, plt_apps_option]}, + {build_and_check, [build_release_plt, plt_apps_option, exclude_and_extra]}, {update, [update_base_plt, update_app_plt]}]. empty_base_plt(Config) -> @@ -275,6 +276,39 @@ plt_apps_option(Config) -> {ok, PltFiles2} = plt_files(Plt), ?assertEqual([App1, App2, erts], get_apps_from_beam_files(PltFiles2)). +exclude_and_extra(Config) -> + AppDir = ?config(apps, Config), + RebarConfig = ?config(rebar_config, Config), + BasePlt = ?config(base_plt, Config), + Plt = ?config(plt, Config), + + {value, {dialyzer, Opts}, Rest} = lists:keytake(dialyzer, 1, RebarConfig), + % Remove erts => [] + % Add erlang+zlib => [erlang, zlib], + % Add erl_prim_loader+init => [erl_prim_loader, init, erlang, zlib] + % Remove zlib+init => [erl_prim_loader, erlang] + Opts2 = [{exclude_apps, [erts]}, + {base_plt_mods, [erlang, zlib]}, + {plt_extra_mods, [erl_prim_loader, init]}, + {exclude_mods, [zlib, init]} | + Opts], + RebarConfig2 = [{dialyzer, Opts2} | Rest], + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]), + + rebar_test_utils:run_and_check(Config, RebarConfig2, ["dialyzer"], + {ok, [{app, Name}]}), + + Erlang = code:where_is_file("erlang.beam"), + {ok, BasePltFiles} = plt_files(BasePlt), + ?assertEqual([Erlang], BasePltFiles), + + Pair = lists:sort([Erlang, code:where_is_file("erl_prim_loader.beam")]), + {ok, PltFiles} = plt_files(Plt), + ?assertEqual(Pair, PltFiles). + %% Helpers erts_files() -> -- cgit v1.1 From 95844531946ab7eabe3bd40df7133b200b292646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 3 Nov 2016 17:02:38 +0100 Subject: Fix usage decription of 'rebar3' --- src/rebar_prv_help.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_prv_help.erl b/src/rebar_prv_help.erl index 75cc609..f34c755 100644 --- a/src/rebar_prv_help.erl +++ b/src/rebar_prv_help.erl @@ -57,7 +57,7 @@ format_error(Reason) -> help(State) -> ?CONSOLE("Rebar3 is a tool for working with Erlang projects.~n~n", []), OptSpecList = rebar3:global_option_spec_list(), - getopt:usage(OptSpecList, "rebar", "", []), + getopt:usage(OptSpecList, "rebar3", "", []), ?CONSOLE("~nSeveral tasks are available:~n", []), providers:help(rebar_state:providers(State)), -- cgit v1.1 From e08145b498a59c1c0c100323646471ea1aa37c41 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 17 Nov 2016 08:57:16 -0500 Subject: Allow rebar3 to edoc itself --- src/rebar_erlc_compiler.erl | 12 +++--------- src/rebar_file_utils.erl | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 36a247e..3711fe9 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -88,14 +88,12 @@ %% 'old_inets'}]}. %% -%% @equiv compile(AppInfo, []). - +%% @equiv compile(AppInfo, []) -spec compile(rebar_app_info:t()) -> ok. compile(AppInfo) when element(1, AppInfo) == app_info_t -> compile(AppInfo, []). %% @doc compile an individual application. - -spec compile(rebar_app_info:t(), compile_opts()) -> ok. compile(AppInfo, CompileOpts) when element(1, AppInfo) == app_info_t -> Dir = ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)), @@ -162,16 +160,14 @@ compile(RebarOpts, BaseDir, OutDir, CompileOpts) -> end, lists:foreach(F, lists:map(fun(SrcDir) -> filename:join(BaseDir, SrcDir) end, ExtraDirs)). -%% @equiv compile_dirs(Context, BaseDir, [Dir], Dir, [{recursive, false}]). - +%% @equiv compile_dirs(Context, BaseDir, [Dir], Dir, [{recursive, false}]) -spec compile_dir(rebar_dict() | rebar_state:t(), file:name(), file:name()) -> ok. compile_dir(State, BaseDir, Dir) when element(1, State) == state_t -> compile_dir(rebar_state:opts(State), BaseDir, Dir, [{recursive, false}]); compile_dir(RebarOpts, BaseDir, Dir) -> compile_dir(RebarOpts, BaseDir, Dir, [{recursive, false}]). -%% @equiv compile_dirs(Context, BaseDir, [Dir], Dir, Opts). - +%% @equiv compile_dirs(Context, BaseDir, [Dir], Dir, Opts) -spec compile_dir(rebar_dict() | rebar_state:t(), file:name(), file:name(), compile_opts()) -> ok. compile_dir(State, BaseDir, Dir, Opts) when element(1, State) == state_t -> compile_dirs(rebar_state:opts(State), BaseDir, [Dir], Dir, Opts); @@ -179,7 +175,6 @@ compile_dir(RebarOpts, BaseDir, Dir, Opts) -> compile_dirs(RebarOpts, BaseDir, [Dir], Dir, Opts). %% @doc compile a list of directories with the given opts. - -spec compile_dirs(rebar_dict() | rebar_state:t(), file:filename(), [file:filename()], @@ -233,7 +228,6 @@ compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> ok. %% @doc remove compiled artifacts from an AppDir. - -spec clean(rebar_app_info:t()) -> 'ok'. clean(AppInfo) -> AppDir = rebar_app_info:out_dir(AppInfo), diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 6721b5a..f2467c5 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -237,10 +237,10 @@ write_file_if_contents_differ(Filename, Bytes) -> %% returns an os appropriate tmpdir given a path -spec system_tmpdir() -> file:filename(). +system_tmpdir() -> system_tmpdir([]). + -spec system_tmpdir(PathComponents) -> file:filename() when PathComponents :: [file:name()]. - -system_tmpdir() -> system_tmpdir([]). system_tmpdir(PathComponents) -> Tmp = case erlang:system_info(system_architecture) of "win32" -> -- cgit v1.1 From e85cf555e24b2a4b9b8681b9028c87826a2c0ea6 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 19 Nov 2016 16:15:43 -0500 Subject: Fix rebar3 dialyzer warnings Some tricky changes in there but should be okay --- src/rebar_app_info.erl | 6 +++--- src/rebar_config.erl | 4 +--- src/rebar_erlc_compiler.erl | 6 ++++-- src/rebar_prv_common_test.erl | 15 ++++----------- src/rebar_prv_cover.erl | 11 +++-------- src/rebar_prv_deps.erl | 2 +- src/rebar_prv_eunit.erl | 14 ++++---------- src/rebar_prv_shell.erl | 2 +- src/rebar_prv_update.erl | 2 +- src/rebar_prv_xref.erl | 2 +- src/rebar_state.erl | 31 +++++++++++++++++++------------ 11 files changed, 42 insertions(+), 53 deletions(-) diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index cf3b82e..4f19d77 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -68,7 +68,7 @@ -export_type([t/0]). --record(app_info_t, {name :: binary(), +-record(app_info_t, {name :: binary() | undefined, app_file_src :: file:filename_all() | undefined, app_file_src_script:: file:filename_all() | undefined, app_file :: file:filename_all() | undefined, @@ -83,11 +83,11 @@ dep_level=0 :: integer(), dir :: file:name(), out_dir :: file:name(), - resource_type :: pkg | src, + resource_type :: pkg | src | undefined, source :: string() | tuple() | checkout | undefined, is_lock=false :: boolean(), is_checkout=false :: boolean(), - valid :: boolean()}). + valid :: boolean() | undefined}). %%============================================================================ %% types diff --git a/src/rebar_config.erl b/src/rebar_config.erl index b50c030..72bc6e9 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -106,9 +106,7 @@ write_lock_file(LockFile, Locks) -> format_attrs([]) -> []; format_attrs([{pkg_hash, Vals}|T]) -> [io_lib:format("{pkg_hash,[~n",[]), format_hashes(Vals), "]}", - maybe_comma(T) | format_attrs(T)]; -format_attrs([H|T]) -> - [io_lib:format("~p~s", [H, maybe_comma(T)]) | format_attrs(T)]. + maybe_comma(T) | format_attrs(T)]. format_hashes([]) -> []; format_hashes([{Pkg,Hash}|T]) -> diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 36a247e..d580792 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -573,9 +573,11 @@ compile_mib(AppInfo) -> MibToHrlOpts = case proplists:get_value(verbosity, AllOpts, undefined) of undefined -> - #options{specific = []}; + #options{specific = [], + cwd = rebar_dir:get_cwd()}; Verbosity -> - #options{specific = [{verbosity, Verbosity}]} + #options{specific = [{verbosity, Verbosity}], + cwd = rebar_dir:get_cwd()} end, ok = snmpc:mib_to_hrl(Mib, Mib, MibToHrlOpts), rebar_file_utils:mv(HrlFilename, AppInclude), diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index b91c90f..be31e8c 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -212,7 +212,6 @@ add_hooks(Opts, State) -> lists:keyreplace(ct_hooks, 1, Opts, {ct_hooks, NewHooks}) end. -select_tests(_, _, {error, _} = Error, _) -> Error; select_tests(_, _, _, {error, _} = Error) -> Error; select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> %% set application env if sys_config argument is provided @@ -293,14 +292,9 @@ compile(State, {ok, _} = Tests) -> compile(_State, Error) -> Error. do_compile(State) -> - case rebar_prv_compile:do(State) of - %% successfully compiled apps - {ok, S} -> - ok = maybe_cover_compile(S), - {ok, S}; - %% this should look like a compiler error, not an eunit error - Error -> Error - end. + {ok, S} = rebar_prv_compile:do(State), + ok = maybe_cover_compile(S), + {ok, S}. inject_ct_state(State, {ok, Tests}) -> Apps = rebar_state:project_apps(State), @@ -308,8 +302,7 @@ inject_ct_state(State, {ok, Tests}) -> {ok, {NewState, ModdedApps}} -> test_dirs(NewState, ModdedApps, Tests); {error, _} = Error -> Error - end; -inject_ct_state(_State, Error) -> Error. + end. inject_ct_state(State, Tests, [App|Rest], Acc) -> case inject(rebar_app_info:opts(App), State, Tests) of diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index b62a796..e53a687 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -88,14 +88,9 @@ analyze(State) -> %% in order for cover data to be reloaded %% this maybe breaks if modules have been deleted %% since code coverage was collected? - case rebar_prv_compile:do(State) of - %% successfully compiled apps - {ok, S} -> - ok = cover_compile(S, apps), - do_analyze(State); - %% this should look like a compiler error, not a cover error - Error -> Error - end. + {ok, S} = rebar_prv_compile:do(State), + ok = cover_compile(S, apps), + do_analyze(State). do_analyze(State) -> ?INFO("Performing cover analysis...", []), diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl index c865276..9ff2bfa 100644 --- a/src/rebar_prv_deps.erl +++ b/src/rebar_prv_deps.erl @@ -97,7 +97,7 @@ display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) -> display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) -> ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); %% Locked -display_dep(State, {Name, Source={pkg, _, Vsn, _}, Level}) when is_integer(Level) -> +display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) -> DepsDir = rebar_dir:deps_dir(State), AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]), NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 82e2458..e85ab4c 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -153,7 +153,6 @@ cfg_tests(State) -> ?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, eunit_tests}}}) end. -select_tests(_State, _ProjectApps, {error, _} = Error, _) -> Error; select_tests(_State, _ProjectApps, _, {error, _} = Error) -> Error; select_tests(State, ProjectApps, [], []) -> {ok, default_tests(State, ProjectApps)}; select_tests(_State, _ProjectApps, [], Tests) -> {ok, Tests}; @@ -316,14 +315,9 @@ inject_test_dir(Opts, Dir) -> compile({error, _} = Error) -> Error; compile(State) -> - case rebar_prv_compile:do(State) of - %% successfully compiled apps - {ok, S} -> - ok = maybe_cover_compile(S), - {ok, S}; - %% this should look like a compiler error, not an eunit error - Error -> Error - end. + {ok, S} = rebar_prv_compile:do(State), + ok = maybe_cover_compile(S), + {ok, S}. validate_tests(State, {ok, Tests}) -> gather_tests(fun(Elem) -> validate(State, Elem) end, Tests, []); @@ -453,7 +447,7 @@ translate(State, [], {dir, Dir}) -> translate(State, [], {file, FilePath}) -> Dir = filename:dirname(FilePath), File = filename:basename(FilePath), - case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(State)) of + case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of {ok, Path} -> {file, filename:join([rebar_dir:base_dir(State), "extras", Path, File])}; %% not relative, leave as is {error, badparent} -> {file, FilePath} diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index b7febf8..31b2e17 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -120,7 +120,7 @@ info() -> setup_shell() -> OldUser = kill_old_user(), %% Test for support here - NewUser = try erlang:open_port({spawn,'tty_sl -c -e'}, []) of + NewUser = try erlang:open_port({spawn,"tty_sl -c -e"}, []) of Port when is_port(Port) -> true = port_close(Port), setup_new_shell() diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 75c609e..54f1796 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -236,7 +236,7 @@ cmp_(BestMatch, MinVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) -> %% We need to treat this differently since we want a version that is LOWER but %% the higest possible one. -cmpl({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) -> +cmpl({_Pkg, _PkgVsn, Dep, _App} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) -> {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State), cmpl_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun). diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl index 3d74c9a..c4e72e7 100644 --- a/src/rebar_prv_xref.erl +++ b/src/rebar_prv_xref.erl @@ -273,7 +273,7 @@ find_function_source(M, F, A, Bin) -> AbstractCode = beam_lib:chunks(Bin, [abstract_code]), {ok, {M, [{abstract_code, {raw_abstract_v1, Code}}]}} = AbstractCode, %% Extract the original source filename from the abstract code - [{attribute, 1, file, {Source, _}} | _] = Code, + [{attribute, _, file, {Source, _}} | _] = Code, %% Extract the line number for a given function def Fn = [E || E <- Code, safe_element(1, E) == function, diff --git a/src/rebar_state.erl b/src/rebar_state.erl index bdd4aeb..9683709 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -59,7 +59,7 @@ command_args = [], command_parsed_args = {[], []}, - current_app :: rebar_app_info:t(), + current_app :: undefined | rebar_app_info:t(), project_apps = [] :: [rebar_app_info:t()], deps_to_build = [] :: [rebar_app_info:t()], all_plugin_deps = [] :: [rebar_app_info:t()], @@ -424,17 +424,24 @@ create_logic_providers(ProviderModules, State0) -> to_list(#state_t{} = State) -> Fields = record_info(fields, state_t), Values = tl(tuple_to_list(State)), - DictSz = tuple_size(dict:new()), - lists:zip(Fields, [reformat(I, DictSz) || I <- Values]). - -reformat({K,V}, DSz) when is_list(V) -> - {K, [reformat(I, DSz) || I <- V]}; -reformat(V, DSz) when is_tuple(V), element(1,V) =:= dict, tuple_size(V) =:= DSz -> - [reformat(I, DSz) || I <- dict:to_list(V)]; -reformat({K,V}, DSz) when is_tuple(V), element(1,V) =:= dict, tuple_size(V) =:= DSz -> - {K, [reformat(I, DSz) || I <- dict:to_list(V)]}; -reformat(Other, _DSz) -> - Other. + lists:zip(Fields, [reformat(I) || I <- Values]). + +reformat({K,V}) when is_list(V) -> + {K, [reformat(I) || I <- V]}; +reformat({K,V}) -> + try + {K, [reformat(I) || I <- dict:to_list(V)]} + catch + error:{badrecord,dict} -> + {K,V} + end; +reformat(V) -> + try + [reformat(I) || I <- dict:to_list(V)] + catch + error:{badrecord,dict} -> + V + end. %% =================================================================== %% Internal functions -- cgit v1.1 From 44fabbbf9173bc97365ccb3c7d28d1bc8503c28d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 18 Nov 2016 17:42:39 +0100 Subject: Add 'recursive' option The option {recursive,boolean()} can now be set pr directory in 'src_dirs' and 'extra_src_dirs', and on top level in the new 'erlc_compiler' option. Example config: {erlc_compiler,[{recursive,false}]}. {src_dirs,[{"src",[{recursive,true}]}]}. This will cause recursive compilation within the "src" directory, but not in any other directoires. --- src/rebar_dir.erl | 60 ++++++++++++++++++++++++++++++++++------ src/rebar_erlc_compiler.erl | 53 ++++++++++++++++++++--------------- src/rebar_prv_eunit.erl | 2 +- test/rebar_compile_SUITE.erl | 42 ++++++++++++++++++++++++++-- test/rebar_dir_SUITE.erl | 66 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 185 insertions(+), 38 deletions(-) diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index 1ec58d4..79a1c7f 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -22,6 +22,7 @@ processing_base_dir/2, make_relative_path/2, src_dirs/1, src_dirs/2, + src_dir_opts/2, recursive/2, extra_src_dirs/1, extra_src_dirs/2, all_src_dirs/1, all_src_dirs/3, retarget_path/2]). @@ -160,28 +161,43 @@ do_make_relative_path(Source, Target) -> Base = lists:duplicate(max(length(Target) - 1, 0), ".."), filename:join(Base ++ Source). +%%%----------------------------------------------------------------- +%%% 'src_dirs' and 'extra_src_dirs' can be configured with options +%%% like this: +%%% +%%% {src_dirs,[{"foo",[{recursive,false}]}]} +%%% {extra_src_dirs,[{"bar",[recursive]}]} (equivalent to {recursive,true}) +%%% +%%% src_dirs/1,2 and extra_src_dirs/1,2 return only the list of +%%% directories for the 'src_dirs' and 'extra_src_dirs' options +%%% respectively, while src_dirs_opts/2 return the options list for +%%% the given directory, no matter if it is configured as 'src_dirs' or +%%% 'extra_src_dirs'. +%%% -spec src_dirs(rebar_dict()) -> list(file:filename_all()). src_dirs(Opts) -> src_dirs(Opts, []). -spec src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()). src_dirs(Opts, Default) -> - ErlOpts = rebar_opts:erl_opts(Opts), - Vs = proplists:get_all_values(src_dirs, ErlOpts), - case lists:append([rebar_opts:get(Opts, src_dirs, []) | Vs]) of - [] -> Default; - Dirs -> lists:usort(Dirs) - end. + src_dirs(src_dirs, Opts, Default). -spec extra_src_dirs(rebar_dict()) -> list(file:filename_all()). extra_src_dirs(Opts) -> extra_src_dirs(Opts, []). -spec extra_src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()). extra_src_dirs(Opts, Default) -> + src_dirs(extra_src_dirs, Opts, Default). + +src_dirs(Type, Opts, Default) -> + lists:usort([case D0 of {D,_} -> D; _ -> D0 end || + D0 <- raw_src_dirs(Type,Opts,Default)]). + +raw_src_dirs(Type, Opts, Default) -> ErlOpts = rebar_opts:erl_opts(Opts), - Vs = proplists:get_all_values(extra_src_dirs, ErlOpts), - case lists:append([rebar_opts:get(Opts, extra_src_dirs, []) | Vs]) of + Vs = proplists:get_all_values(Type, ErlOpts), + case lists:append([rebar_opts:get(Opts, Type, []) | Vs]) of [] -> Default; - Dirs -> lists:usort(Dirs) + Dirs -> Dirs end. -spec all_src_dirs(rebar_dict()) -> list(file:filename_all()). @@ -192,6 +208,32 @@ all_src_dirs(Opts) -> all_src_dirs(Opts, [], []). all_src_dirs(Opts, SrcDefault, ExtraDefault) -> lists:usort(src_dirs(Opts, SrcDefault) ++ extra_src_dirs(Opts, ExtraDefault)). +%%%----------------------------------------------------------------- +%%% Return the list of options for the given src directory +%%% If the same option is given multiple times for a directory in the +%%% config, the priority order is: first occurence of 'src_dirs' +%%% followed by first occurence of 'extra_src_dirs'. +-spec src_dir_opts(rebar_dict(), file:filename_all()) -> [{atom(),term()}]. +src_dir_opts(Opts, Dir) -> + RawSrcDirs = raw_src_dirs(src_dirs, Opts, []), + RawExtraSrcDirs = raw_src_dirs(extra_src_dirs, Opts, []), + AllOpts = [Opt || {D,Opt} <- RawSrcDirs++RawExtraSrcDirs, + D==Dir], + lists:ukeysort(1,proplists:unfold(lists:append(AllOpts))). + +%%%----------------------------------------------------------------- +%%% Return the value of the 'recursive' option for the given directory. +%%% If not given, the value of 'recursive' in the 'erlc_compiler' +%%% options is used, and finally the default is 'true'. +-spec recursive(rebar_dict(), file:filename_all()) -> boolean(). +recursive(Opts, Dir) -> + DirOpts = src_dir_opts(Opts, Dir), + Default = proplists:get_value(recursive, + rebar_opts:get(Opts, erlc_compiler, []), + true), + R = proplists:get_value(recursive, DirOpts, Default), + R. + %% given a path if that path is an ancestor of an app dir return the path relative to that %% apps outdir. if the path is not an ancestor to any app dirs but is an ancestor of the %% project root return the path relative to the project base_dir. if it is not an ancestor diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 97235aa..325bb4f 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -47,10 +47,6 @@ -type compile_opts() :: [compile_opt()]. -type compile_opt() :: {recursive, boolean()}. --record(compile_opts, { - recursive = true -}). - -define(DEFAULT_OUTDIR, "ebin"). -define(RE_PREFIX, "^[^._]"). @@ -99,21 +95,25 @@ compile(AppInfo, CompileOpts) when element(1, AppInfo) == app_info_t -> Dir = ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)), RebarOpts = rebar_app_info:opts(AppInfo), + SrcOpts = [check_last_mod, + {recursive, dir_recursive(RebarOpts, "src", CompileOpts)}], + MibsOpts = [check_last_mod, + {recursive, dir_recursive(RebarOpts, "mibs", CompileOpts)}], rebar_base_compiler:run(RebarOpts, check_files([filename:join(Dir, File) || File <- rebar_opts:get(RebarOpts, xrl_first_files, [])]), filename:join(Dir, "src"), ".xrl", filename:join(Dir, "src"), ".erl", - fun compile_xrl/3), + fun compile_xrl/3, SrcOpts), rebar_base_compiler:run(RebarOpts, check_files([filename:join(Dir, File) || File <- rebar_opts:get(RebarOpts, yrl_first_files, [])]), filename:join(Dir, "src"), ".yrl", filename:join(Dir, "src"), ".erl", - fun compile_yrl/3), + fun compile_yrl/3, SrcOpts), rebar_base_compiler:run(RebarOpts, check_files([filename:join(Dir, File) || File <- rebar_opts:get(RebarOpts, mib_first_files, [])]), filename:join(Dir, "mibs"), ".mib", filename:join([Dir, "priv", "mibs"]), ".bin", - compile_mib(AppInfo)), + compile_mib(AppInfo), MibsOpts), SrcDirs = lists:map(fun(SrcDir) -> filename:join(Dir, SrcDir) end, rebar_dir:src_dirs(RebarOpts, ["src"])), @@ -182,13 +182,10 @@ compile_dir(RebarOpts, BaseDir, Dir, Opts) -> compile_opts()) -> ok. compile_dirs(State, BaseDir, Dirs, OutDir, CompileOpts) when element(1, State) == state_t -> compile_dirs(rebar_state:opts(State), BaseDir, Dirs, OutDir, CompileOpts); -compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) -> - CompileOpts = parse_opts(Opts), - +compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, CompileOpts) -> ErlOpts = rebar_opts:erl_opts(RebarOpts), ?DEBUG("erlopts ~p", [ErlOpts]), - Recursive = CompileOpts#compile_opts.recursive, - AllErlFiles = gather_src(SrcDirs, Recursive), + AllErlFiles = gather_src(RebarOpts, BaseDir, SrcDirs, CompileOpts), ?DEBUG("files to compile ~p", [AllErlFiles]), %% Make sure that outdir is on the path @@ -266,12 +263,22 @@ clean_dirs(AppDir, [Dir|Rest]) -> %% Internal functions %% =================================================================== -gather_src(Dirs, Recursive) -> - gather_src(Dirs, [], Recursive). - -gather_src([], Srcs, _Recursive) -> Srcs; -gather_src([Dir|Rest], Srcs, Recursive) -> - gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, ?RE_PREFIX".*\\.erl\$", Recursive), Recursive). +gather_src(Opts, BaseDir, Dirs, CompileOpts) -> + gather_src(Opts, filename:split(BaseDir), Dirs, [], CompileOpts). + +gather_src(_Opts, _BaseDirParts, [], Srcs, _CompileOpts) -> Srcs; +gather_src(Opts, BaseDirParts, [Dir|Rest], Srcs, CompileOpts) -> + DirParts = filename:split(Dir), + RelDir = case lists:prefix(BaseDirParts,DirParts) of + true -> + case lists:nthtail(length(BaseDirParts),DirParts) of + [] -> "."; + RestParts -> filename:join(RestParts) + end; + false -> Dir + end, + DirRecursive = dir_recursive(Opts, RelDir, CompileOpts), + gather_src(Opts, BaseDirParts, Rest, Srcs ++ rebar_utils:find_files(Dir, ?RE_PREFIX".*\\.erl\$", DirRecursive), CompileOpts). %% 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 @@ -758,8 +765,8 @@ include_abs_dirs(ErlOpts, BaseDir) -> InclDirs = ["include"|proplists:get_all_values(i, ErlOpts)], lists:map(fun(Incl) -> filename:join([BaseDir, Incl]) end, InclDirs). -parse_opts(Opts) -> parse_opts(Opts, #compile_opts{}). - -parse_opts([], CompileOpts) -> CompileOpts; -parse_opts([{recursive, Recursive}|Rest], CompileOpts) when Recursive == true; Recursive == false -> - parse_opts(Rest, CompileOpts#compile_opts{recursive = Recursive}). +dir_recursive(Opts, Dir, CompileOpts) when is_list(CompileOpts) -> + case proplists:get_value(recursive,CompileOpts) of + undefined -> rebar_dir:recursive(Opts, Dir); + Recursive -> Recursive + end. diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index e85ab4c..0908ec9 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -310,7 +310,7 @@ maybe_inject_test_dir(State, AppAcc, [], Dir) -> inject_test_dir(Opts, Dir) -> %% append specified test targets to app defined `extra_src_dirs` - ExtraSrcDirs = rebar_dir:extra_src_dirs(Opts), + ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). compile({error, _} = Error) -> Error; diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index f31ab39..3e4d5b9 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -46,7 +46,8 @@ include_file_in_src_test/1, always_recompile_when_erl_compiler_options_set/1, recompile_when_parse_transform_inline_changes/1, - recompile_when_parse_transform_as_opt_changes/1]). + recompile_when_parse_transform_as_opt_changes/1, + recursive/1,no_recursive/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -72,7 +73,8 @@ all() -> include_file_relative_to_working_directory, include_file_in_src, include_file_relative_to_working_directory_test, include_file_in_src_test, recompile_when_parse_transform_as_opt_changes, - recompile_when_parse_transform_inline_changes] ++ + recompile_when_parse_transform_inline_changes, + recursive, no_recursive] ++ case erlang:function_exported(os, unsetenv, 1) of true -> [always_recompile_when_erl_compiler_options_set]; false -> [] @@ -1520,3 +1522,39 @@ recompile_when_parse_transform_as_opt_changes(Config) -> ?assert(ModTime =/= NewModTime). +recursive(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:write_src_file(filename:join(AppDir,src),"rec.erl"), + + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), + + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + {ok, Files} = rebar_utils:list_dir(EbinDir), + ?assert(lists:member("rec.beam",Files)). + +no_recursive(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:write_src_file(filename:join(AppDir,src),"rec.erl"), + + RebarConfig1 = [{erlc_compiler,[{recursive,false}]}], + rebar_test_utils:run_and_check(Config, RebarConfig1, ["compile"], + {ok, [{app, Name}]}), + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + {ok, Files1} = rebar_utils:list_dir(EbinDir), + ?assert(false==lists:member("rec.beam",Files1)), + + RebarConfig2 = [{src_dirs,[{"src",[{recursive,false}]}]}], + rebar_test_utils:run_and_check(Config, RebarConfig2, ["compile"], + {ok, [{app, Name}]}), + {ok, Files2} = rebar_utils:list_dir(EbinDir), + ?assert(false==lists:member("rec.beam",Files2)), + + ok. diff --git a/test/rebar_dir_SUITE.erl b/test/rebar_dir_SUITE.erl index 9734830..6797802 100644 --- a/test/rebar_dir_SUITE.erl +++ b/test/rebar_dir_SUITE.erl @@ -3,8 +3,10 @@ -export([all/0, init_per_testcase/2, end_per_testcase/2]). -export([default_src_dirs/1, default_extra_src_dirs/1, default_all_src_dirs/1]). --export([src_dirs/1, extra_src_dirs/1, all_src_dirs/1]). +-export([src_dirs/1, src_dirs_with_opts/1, extra_src_dirs/1, all_src_dirs/1]). +-export([src_dir_opts/1, recursive/1]). -export([profile_src_dirs/1, profile_extra_src_dirs/1, profile_all_src_dirs/1]). +-export([profile_src_dir_opts/1]). -export([retarget_path/1, alt_base_dir_abs/1, alt_base_dir_rel/1]). -export([global_cache_dir/1, default_global_cache_dir/1, overwrite_default_global_cache_dir/1]). @@ -14,8 +16,9 @@ all() -> [default_src_dirs, default_extra_src_dirs, default_all_src_dirs, - src_dirs, extra_src_dirs, all_src_dirs, + src_dirs, extra_src_dirs, all_src_dirs, src_dir_opts, recursive, profile_src_dirs, profile_extra_src_dirs, profile_all_src_dirs, + profile_src_dir_opts, retarget_path, alt_base_dir_abs, alt_base_dir_rel, global_cache_dir, default_global_cache_dir, overwrite_default_global_cache_dir]. @@ -70,6 +73,13 @@ src_dirs(Config) -> ["bar", "baz", "foo"] = rebar_dir:src_dirs(rebar_state:opts(State)). +src_dirs_with_opts(Config) -> + RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar", "baz"]}, + {src_dirs, [{"foo",[{recursive,false}]}, "qux"]}]}], + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + ["bar", "baz", "foo", "qux"] = rebar_dir:src_dirs(rebar_state:opts(State)). + extra_src_dirs(Config) -> RebarConfig = [{erl_opts, [{extra_src_dirs, ["foo", "bar", "baz"]}]}], {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), @@ -77,11 +87,41 @@ extra_src_dirs(Config) -> ["bar", "baz", "foo"] = rebar_dir:extra_src_dirs(rebar_state:opts(State)). all_src_dirs(Config) -> - RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}, {extra_src_dirs, ["baz", "qux"]}]}], + RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}, {extra_src_dirs, ["baz", "qux"]}, {src_dirs, [{"foo", [{recursive,false}]}]}]}], {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), ["bar", "baz", "foo", "qux"] = rebar_dir:all_src_dirs(rebar_state:opts(State)). +src_dir_opts(Config) -> + RebarConfig = + [{erl_opts, [{src_dirs, [{"foo",[{recursive,true}]}, "bar"]}, + {extra_src_dirs, ["baz", {"foo", [{recursive,false}]}]}, + {src_dirs, [{"foo", [{recursive,false}]}]}]}], + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, + ["compile"], return), + [{recursive,true}] = rebar_dir:src_dir_opts(rebar_state:opts(State), "foo"), + [] = rebar_dir:src_dir_opts(rebar_state:opts(State), "bar"), + [] = rebar_dir:src_dir_opts(rebar_state:opts(State), "nonexisting"). + +recursive(Config) -> + RebarConfig1 = + [{erl_opts, [{src_dirs, ["foo", "bar"]}, + {extra_src_dirs, ["baz", {"foo", [{recursive,true}]}]}, + {src_dirs, [{"foo", [{recursive,false}]}]}]}], + {ok, State1} = rebar_test_utils:run_and_check(Config, RebarConfig1, + ["compile"], return), + false = rebar_dir:recursive(rebar_state:opts(State1), "foo"), + true = rebar_dir:recursive(rebar_state:opts(State1), "bar"), + + RebarConfig2 = [{erlc_compiler,[{recursive,false}]}, + {erl_opts,[{src_dirs,["foo",{"bar",[{recursive,true}]}]}]}], + {ok, State2} = rebar_test_utils:run_and_check(Config, RebarConfig2, + ["compile"], return), + false = rebar_dir:recursive(rebar_state:opts(State2), "foo"), + true = rebar_dir:recursive(rebar_state:opts(State2), "bar"), + + ok. + profile_src_dirs(Config) -> RebarConfig = [ {erl_opts, [{src_dirs, ["foo", "bar"]}]}, @@ -118,6 +158,26 @@ profile_all_src_dirs(Config) -> R = lists:sort(["foo", "bar", "baz", "qux"]), R = rebar_dir:all_src_dirs(rebar_state:opts(State)). +profile_src_dir_opts(Config) -> + RebarConfig = [ + {erl_opts, [{src_dirs, ["foo"]}, + {extra_src_dirs, [{"bar",[recursive]}]}]}, + {profiles, [ + {more, [{erl_opts, [{src_dirs, [{"bar",[{recursive,false}]}]}, + {extra_src_dirs, ["qux"]}]}]} + ]} + ], + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, + ["as", "more", "compile"], + return), + + [{recursive,false}] = rebar_dir:src_dir_opts(rebar_state:opts(State),"bar"), + + {ok, State1} = rebar_test_utils:run_and_check(Config, RebarConfig, + ["compile"], return), + + [{recursive,true}] = rebar_dir:src_dir_opts(rebar_state:opts(State1),"bar"). + retarget_path(Config) -> {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return), -- cgit v1.1 From aa0c80d62f90e8b69e50d1f516f82f137e066bd9 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Thu, 10 Nov 2016 12:05:56 -0800 Subject: upload rebar3 escript for every merge to master to rebar3-nightly --- .travis.yml | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e17e50..48fef3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,10 +16,24 @@ cache: directories: - "$HOME/.cache/rebar3/hex/default" deploy: - provider: releases - api_key: - secure: MjloYuaQF3cd3Oab57zqwPDLPqt5MDgBIrRLpXOQwNovr2tnkKd4aJK3QJ3pTxvZievjgl+qIYI1IZyjuRV37nkjAfMw14iig959wi0k8XTJoMdylVxE5X7hk4SiWhX/ycnJx3C28PPw1OitGTF76HAJDMgEelNdoNt+hvjvDEo= - file: rebar3 - on: - repo: erlang/rebar3 - tags: true + - provider: releases + api_key: + secure: MjloYuaQF3cd3Oab57zqwPDLPqt5MDgBIrRLpXOQwNovr2tnkKd4aJK3QJ3pTxvZievjgl+qIYI1IZyjuRV37nkjAfMw14iig959wi0k8XTJoMdylVxE5X7hk4SiWhX/ycnJx3C28PPw1OitGTF76HAJDMgEelNdoNt+hvjvDEo= + file: rebar3 + on: + repo: erlang/rebar3 + tags: true + + - provider: s3 + region: us-west-2 + access_key_id: AKIAJAPYAQEFYCYSNL7Q + secret_access_key: + secure: "BUv2KQABv0Q4e8DAVNBRTc/lXHWt27yCN46Fdgo1IrcSSIiP+hq2yXzQcXLbPwkEu6pxUZQtL3mvKbt6l7uw3wFrcRfFAi1PGTITAW8MTmxtwcZIBcHSk3XOzDbkK+fYYcaddszmt7hDzzEFPtmYXiNgnaMIVeynhQLgcCcIRRQ=" + skip_cleanup: true + local-dir: _build/default/bin + bucket: "rebar3-nightly" + acl: public_read + on: + repo: erlang/rebar3 + branch: master + condition: $TRAVIS_OTP_RELEASE = "R16B03-1" -- cgit v1.1 From 5323fdc377d6034cdb625547600144881ae1af46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20Meadows-J=C3=B6nsson?= Date: Tue, 22 Nov 2016 22:22:05 +0100 Subject: Always read REBAR_CONFIG env var when loading config --- src/rebar.hrl | 1 - src/rebar3.erl | 7 +------ src/rebar_config.erl | 19 +++++++++++++++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/rebar.hrl b/src/rebar.hrl index f96ed5e..c94a84a 100644 --- a/src/rebar.hrl +++ b/src/rebar.hrl @@ -22,7 +22,6 @@ -define(DEFAULT_PLUGINS_DIR, "plugins"). -define(DEFAULT_TEST_DEPS_DIR, "test/lib"). -define(DEFAULT_RELEASE_DIR, "rel"). --define(DEFAULT_CONFIG_FILE, "rebar.config"). -define(CONFIG_VERSION, "1.1.0"). -define(DEFAULT_CDN, "https://repo.hex.pm/"). -define(REMOTE_PACKAGE_DIR, "tarballs"). diff --git a/src/rebar3.erl b/src/rebar3.erl index 47dc25a..4e7e284 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -149,12 +149,7 @@ init_config() -> Verbosity = log_level(), ok = rebar_log:init(command_line, Verbosity), - Config = case os:getenv("REBAR_CONFIG") of - false -> - rebar_config:consult_file(?DEFAULT_CONFIG_FILE); - ConfigFile -> - rebar_config:consult_file(ConfigFile) - end, + Config = rebar_config:consult(), Config1 = rebar_config:merge_locks(Config, rebar_config:consult_lock_file(?LOCK_FILE)), %% If $HOME/.config/rebar3/rebar.config exists load and use as global config diff --git a/src/rebar_config.erl b/src/rebar_config.erl index b50c030..b2db868 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -26,7 +26,8 @@ %% ------------------------------------------------------------------- -module(rebar_config). --export([consult/1 +-export([consult/0 + ,consult/1 ,consult_app_file/1 ,consult_file/1 ,consult_lock_file/1 @@ -39,13 +40,19 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). +-define(DEFAULT_CONFIG_FILE, "rebar.config"). + %% =================================================================== %% Public API %% =================================================================== +-spec consult() -> [any()]. +consult() -> + consult_file(config_file()). + -spec consult(file:name()) -> [any()]. consult(Dir) -> - consult_file(filename:join(Dir, ?DEFAULT_CONFIG_FILE)). + consult_file(filename:join(Dir, config_file())). consult_app_file(File) -> consult_file_(File). @@ -300,3 +307,11 @@ check_newly_added_(Dep, LockedDeps) when is_atom(Dep) -> end; check_newly_added_(Dep, _) -> throw(?PRV_ERROR({bad_dep_name, Dep})). + +config_file() -> + case os:getenv("REBAR_CONFIG") of + false -> + ?DEFAULT_CONFIG_FILE; + ConfigFile -> + ConfigFile + end. -- cgit v1.1 From 8f72d534b01bdc48a01d618d2a73e031ae4046d3 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Tue, 22 Nov 2016 14:17:16 -0800 Subject: use default region for s3 deployment --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 48fef3c..ff4a252 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ deploy: tags: true - provider: s3 - region: us-west-2 access_key_id: AKIAJAPYAQEFYCYSNL7Q secret_access_key: secure: "BUv2KQABv0Q4e8DAVNBRTc/lXHWt27yCN46Fdgo1IrcSSIiP+hq2yXzQcXLbPwkEu6pxUZQtL3mvKbt6l7uw3wFrcRfFAi1PGTITAW8MTmxtwcZIBcHSk3XOzDbkK+fYYcaddszmt7hDzzEFPtmYXiNgnaMIVeynhQLgcCcIRRQ=" -- cgit v1.1 From 62e1aaf6f5a58ac60871248c193c20d5c1e7bbc5 Mon Sep 17 00:00:00 2001 From: Nathaniel Waisbrot Date: Wed, 23 Nov 2016 09:19:19 -0500 Subject: expect the `missing_package` error to have arity 2 or 3 --- src/rebar_packages.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl index 8b4611b..4cce5a8 100644 --- a/src/rebar_packages.erl +++ b/src/rebar_packages.erl @@ -216,7 +216,7 @@ handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint) -> {ok, Vsn} end. -format_error({missing_package, {Name, Vsn}}) -> +format_error({missing_package, Name, Vsn}) -> io_lib:format("Package not found in registry: ~s-~s.", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]); format_error({missing_package, Dep}) -> io_lib:format("Package not found in registry: ~p.", [Dep]). -- cgit v1.1 From 64eab7c57c8ba7a27fefa7c3062cc237dd3f5d80 Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Sun, 27 Nov 2016 21:02:16 +0000 Subject: Upgrade relx, erlware_commons and cf relx ~> 3.22.0 erlware_commons ~> 0.22.0 cf ~> 0.2.2 --- rebar.config | 6 +++--- rebar.lock | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rebar.config b/rebar.config index f61d3e5..a48fa41 100644 --- a/rebar.config +++ b/rebar.config @@ -1,14 +1,14 @@ %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -{deps, [{erlware_commons, "0.21.0"}, +{deps, [{erlware_commons, "0.22.0"}, {ssl_verify_fun, "1.1.1"}, {certifi, "0.4.0"}, {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.3.0"}, - {relx, "3.21.1"}, - {cf, "0.2.1"}, + {relx, "3.22.0"}, + {cf, "0.2.2"}, {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. diff --git a/rebar.lock b/rebar.lock index 8f0f659..f9a48f9 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,24 +1,24 @@ {"1.1.0", [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.3.0">>},0}, {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, - {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.2.2">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.22.0">>},0}, {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.21.1">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.22.0">>},0}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ {<<"bbmustache">>, <<"2010ADAE78830992A4C69680115ECD7D475DD03A72C076BBADDCCBF2D4B32035">>}, {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, - {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>}, + {<<"cf">>, <<"7F2913FFF90ABCABD0F489896CFEB0B0674F6C8DF6C10B17A83175448029896C">>}, {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, - {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>}, + {<<"erlware_commons">>, <<"051BED79A34E66678C1CBEEBC7B66360C827D081A0C5E2528878011E31DDCDCA">>}, {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"F989DC520730EFD9075E9F4DEBCB8BA1D7D1E86B018B0BCF45A2EB80270B4AD6">>}, + {<<"relx">>, <<"FF7E2B5924B754A63BA1A46BA8C1901A2D6AE1418982E189CFED5937DACE18DA">>}, {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} ]. -- cgit v1.1 From 3da4cc222197e01886d3baaeca7a380e02ff3125 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 25 Nov 2016 21:32:43 -0500 Subject: Type specifications and edocs improvements Includes improvments and function documentation for all modules (in alphabetical order) up to rebar_core, and may have included more in other modules as I saw fit to dig and understand more of the internals. --- src/r3.erl | 7 ++- src/rebar3.erl | 76 +++++++++++++++++++----- src/rebar_agent.erl | 59 +++++++++++++++---- src/rebar_api.erl | 38 +++++++++--- src/rebar_app_discover.erl | 81 +++++++++++++++++++++++-- src/rebar_app_info.erl | 131 ++++++++++++++++++++++++++++++++++++----- src/rebar_app_utils.erl | 79 ++++++++++++++++++++++++- src/rebar_base_compiler.erl | 108 +++++++++++++++++++++++++++++++++ src/rebar_config.erl | 82 ++++++++++++++++++++++++-- src/rebar_core.erl | 34 ++++++++++- src/rebar_dir.erl | 20 +++++++ src/rebar_dist_utils.erl | 2 +- src/rebar_file_utils.erl | 6 +- src/rebar_pkg_resource.erl | 6 ++ src/rebar_prv_install_deps.erl | 9 ++- src/rebar_utils.erl | 23 ++++++-- 16 files changed, 686 insertions(+), 75 deletions(-) diff --git a/src/r3.erl b/src/r3.erl index 5e8b26d..d0d6c47 100644 --- a/src/r3.erl +++ b/src/r3.erl @@ -1,7 +1,12 @@ -%%% external alias for rebar_agent +%%% @doc external alias for `rebar_agent' for more convenient +%%% calls from a shell. -module(r3). -export([do/1, do/2]). +%% @doc alias for `rebar_agent:do/1' +-spec do(atom()) -> ok | {error, term()}. do(Command) -> rebar_agent:do(Command). +%% @doc alias for `rebar_agent:do/2' +-spec do(atom(), atom()) -> ok | {error, term()}. do(Namespace, Command) -> rebar_agent:do(Namespace, Command). diff --git a/src/rebar3.erl b/src/rebar3.erl index 4e7e284..fa26ab2 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -24,6 +24,16 @@ %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- +%% +%% @doc Main module for rebar3. Supports two interfaces; one for escripts, +%% and one for usage as a library (although rebar3 makes a lot of +%% assumptions about its environment, making it a bit tricky to use as +%% a lib). +%% +%% This module's job is mostly to set up the root environment for rebar3 +%% and handle global options (mostly all from the ENV) and make them +%% accessible to the rest of the run. +%% @end -module(rebar3). -export([main/0, @@ -43,14 +53,14 @@ %% Public API %% ==================================================================== -%% For running with: +%% @doc For running with: %% erl +sbtu +A0 -noinput -mode minimal -boot start_clean -s rebar3 main -extra "$@" -spec main() -> no_return(). main() -> List = init:get_plain_arguments(), main(List). -%% escript Entry point +%% @doc escript Entry point -spec main(list()) -> no_return(). main(Args) -> try run(Args) of @@ -63,7 +73,8 @@ main(Args) -> handle_error(Error) end. -%% Erlang-API entry point +%% @doc Erlang-API entry point +-spec run(rebar_state:t(), [string()]) -> {ok, rebar_state:t()} | {error, term()}. run(BaseState, Commands) -> start_and_load_apps(api), BaseState1 = rebar_state:set(BaseState, task, Commands), @@ -78,6 +89,10 @@ run(BaseState, Commands) -> %% Internal functions %% ==================================================================== +%% @private sets up the rebar3 environment based on the command line +%% arguments passed, if they have any relevance; used to translate +%% from the escript call-site into a common one with the library +%% usage. run(RawArgs) -> start_and_load_apps(command_line), @@ -95,7 +110,13 @@ run(RawArgs) -> {BaseState2, _Args1} = set_options(BaseState1, {[], []}), run_aux(BaseState2, RawArgs). +%% @private Junction point between the CLI and library entry points. +%% From here on the module's role is a shared path here to finish +%% up setting the environment for the run. +-spec run_aux(rebar_state:t(), [string()]) -> + {ok, rebar_state:t()} | {error, term()}. run_aux(State, RawArgs) -> + %% Profile override; can only support one profile State1 = case os:getenv("REBAR_PROFILE") of false -> State; @@ -108,6 +129,7 @@ run_aux(State, RawArgs) -> rebar_utils:check_min_otp_version(rebar_state:get(State1, minimum_otp_vsn, undefined)), rebar_utils:check_blacklisted_otp_versions(rebar_state:get(State1, blacklisted_otp_vsns, undefined)), + %% Change the default hex CDN State2 = case os:getenv("HEX_CDN") of false -> State1; @@ -144,6 +166,9 @@ run_aux(State, RawArgs) -> rebar_core:init_command(rebar_state:command_args(State10, Args), Task). +%% @doc set up base configuration having to do with verbosity, where +%% to find config files, and so on, and return an internal rebar3 state term. +-spec init_config() -> rebar_state:t(). init_config() -> %% Initialize logging system Verbosity = log_level(), @@ -188,6 +213,17 @@ init_config() -> %% Initialize vsn cache rebar_state:set(State1, vsn_cache, dict:new()). +%% @doc Parse basic rebar3 arguments to find the top-level task +%% to be run; this parsing is only partial from the point of view that +%% runs done with arguments like `as $PROFILE do $TASK' will just +%% return `as', which is then in charge of doing a more dynamic +%% dispatch. +%% If no arguments are given, the `help' task is returned. +%% If special arguments like `-h' or `-v' are translated to `help' +%% and `version' tasks. +%% The unparsed parts of arguments are returned in: +%% `{Task, Rest}'. +-spec parse_args([string()]) -> {atom(), [string()]}. parse_args([]) -> parse_args(["help"]); parse_args([H | Rest]) when H =:= "-h" @@ -199,6 +235,7 @@ parse_args([H | Rest]) when H =:= "-v" parse_args([Task | RawRest]) -> {list_to_atom(Task), RawRest}. +%% @private actually not too sure what this does anymore. set_options(State, {Options, NonOptArgs}) -> GlobalDefines = proplists:get_all_values(defines, Options), @@ -211,9 +248,8 @@ set_options(State, {Options, NonOptArgs}) -> {rebar_state:set(State2, task, Task), NonOptArgs}. -%% -%% get log level based on getopt option -%% +%% @doc get log level based on getopt options and ENV +-spec log_level() -> integer(). log_level() -> case os:getenv("QUIET") of Q when Q == false; Q == "" -> @@ -228,18 +264,16 @@ log_level() -> rebar_log:error_level() end. -%% -%% show version information and halt -%% +%% @doc show version information +-spec version() -> ok. version() -> {ok, Vsn} = application:get_key(rebar, vsn), ?CONSOLE("rebar ~s on Erlang/OTP ~s Erts ~s", [Vsn, erlang:system_info(otp_release), erlang:system_info(version)]). +%% @private set global flag based on getopt option boolean value %% TODO: Actually make it 'global' -%% -%% set global flag based on getopt option boolean value -%% +-spec set_global_flag(rebar_state:t(), list(), term()) -> rebar_state:t(). set_global_flag(State, Options, Flag) -> Value = case proplists:get_bool(Flag, Options) of true -> @@ -249,9 +283,9 @@ set_global_flag(State, Options, Flag) -> end, rebar_state:set(State, Flag, Value). -%% -%% options accepted via getopt -%% + +%% @doc options accepted via getopt +-spec global_option_spec_list() -> [{atom(), char(), string(), atom(), string()}, ...]. global_option_spec_list() -> [ %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} @@ -260,6 +294,9 @@ global_option_spec_list() -> {task, undefined, undefined, string, "Task to run."} ]. +%% @private translate unhandled errors and internal return codes into proper +%% erroneous program exits. +-spec handle_error(term()) -> no_return(). handle_error(rebar_abort) -> erlang:halt(1); handle_error({error, rebar_abort}) -> @@ -293,6 +330,11 @@ handle_error(Error) -> ?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []), erlang:halt(1). +%% @private Boot Erlang dependencies; problem is that escripts don't auto-boot +%% stuff the way releases do and we have to do it by hand. +%% This also lets us detect and show nicer errors when a critical lib is +%% not supported +-spec start_and_load_apps(command_line|api) -> term(). start_and_load_apps(Caller) -> _ = application:load(rebar), %% Make sure crypto is running @@ -303,6 +345,9 @@ start_and_load_apps(Caller) -> inets:start(), inets:start(httpc, [{profile, rebar}]). +%% @doc Make sure a required app is running, or display an error message +%% and abort if there's a problem. +-spec ensure_running(atom(), command_line|api) -> ok | no_return(). ensure_running(App, Caller) -> case application:start(App) of ok -> ok; @@ -319,6 +364,7 @@ ensure_running(App, Caller) -> throw(rebar_abort) end. +-spec state_from_global_config([term()], file:filename()) -> rebar_state:t(). state_from_global_config(Config, GlobalConfigFile) -> rebar_utils:set_httpc_options(), GlobalConfigTerms = rebar_config:consult_file(GlobalConfigFile), diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 4b0fc5f..ed9e45d 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -1,3 +1,5 @@ +%%% @doc Runs a process that holds a rebar3 state and can be used +%%% to statefully maintain loaded project state into a running VM. -module(rebar_agent). -export([start_link/1, do/1, do/2]). -export([init/1, @@ -10,19 +12,34 @@ cwd, show_warning=true}). +%% @doc boots an agent server; requires a full rebar3 state already. +%% By default (within rebar3), this isn't called; `rebar_prv_shell' +%% enters and transforms into this module +-spec start_link(rebar_state:t()) -> {ok, pid()}. start_link(State) -> gen_server:start_link({local, ?MODULE}, ?MODULE, State, []). +%% @doc runs a given command in the agent's context. +-spec do(atom()) -> ok | {error, term()}. do(Command) when is_atom(Command) -> gen_server:call(?MODULE, {cmd, Command}, infinity). +%% @doc runs a given command in the agent's context, under a given +%% namespace. +-spec do(atom(), atom()) -> ok | {error, term()}. do(Namespace, Command) when is_atom(Namespace), is_atom(Command) -> gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity). +%%%%%%%%%%%%%%%%% +%%% CALLBACKS %%% +%%%%%%%%%%%%%%%%% + +%% @private init(State) -> Cwd = rebar_dir:get_cwd(), {ok, #state{state=State, cwd=Cwd}}. +%% @private handle_call({cmd, Command}, _From, State=#state{state=RState, cwd=Cwd}) -> MidState = maybe_show_warning(State), {Res, NewRState} = run(default, Command, RState, Cwd), @@ -34,18 +51,29 @@ handle_call({cmd, Namespace, Command}, _From, State = #state{state=RState, cwd=C handle_call(_Call, _From, State) -> {noreply, State}. +%% @private handle_cast(_Cast, State) -> {noreply, State}. +%% @private handle_info(_Info, State) -> {noreply, State}. +%% @private code_change(_OldVsn, State, _Extra) -> {ok, State}. +%% @private terminate(_Reason, _State) -> ok. +%%%%%%%%%%%%%%% +%%% PRIVATE %%% +%%%%%%%%%%%%%%% + +%% @private runs the actual command and maintains the state changes +-spec run(atom(), atom(), rebar_state:t(), file:filename()) -> + {ok, rebar_state:t()} | {{error, term()}, rebar_state:t()}. run(Namespace, Command, RState, Cwd) -> try case rebar_dir:get_cwd() of @@ -74,12 +102,17 @@ run(Namespace, Command, RState, Cwd) -> {{error, {Type, Reason}}, RState} end. +%% @private function to display a warning for the feature only once +-spec maybe_show_warning(#state{}) -> #state{}. maybe_show_warning(S=#state{show_warning=true}) -> ?WARN("This feature is experimental and may be modified or removed at any time.", []), S#state{show_warning=false}; maybe_show_warning(State) -> State. +%% @private based on a rebar3 state term, reload paths in a way +%% that makes sense. +-spec refresh_paths(rebar_state:t()) -> ok. refresh_paths(RState) -> ToRefresh = (rebar_state:code_paths(RState, all_deps) ++ [filename:join([rebar_app_info:out_dir(App), "test"]) @@ -116,6 +149,9 @@ refresh_paths(RState) -> end end, ToRefresh). +%% @private from a disk config, reload and reapply with the current +%% profiles; used to find changes in the config from a prior run. +-spec refresh_state(rebar_state:t(), file:filename()) -> rebar_state:t(). refresh_state(RState, _Dir) -> lists:foldl( fun(F, State) -> F(State) end, @@ -123,26 +159,28 @@ refresh_state(RState, _Dir) -> [fun(S) -> rebar_state:apply_profiles(S, rebar_state:current_profiles(RState)) end] ). +%% @private takes a list of modules and reloads them +-spec reload_modules([module()]) -> term(). reload_modules([]) -> noop; -reload_modules(Modules) -> +reload_modules(Modules) -> reload_modules(Modules, erlang:function_exported(code, prepare_loading, 1)). -%% OTP 19 and later -- use atomic loading and ignore unloadable mods +%% @private reloading modules, when there are modules to actually reload reload_modules(Modules, true) -> + %% OTP 19 and later -- use atomic loading and ignore unloadable mods case code:prepare_loading(Modules) of {ok, Prepared} -> [code:purge(M) || M <- Modules], code:finish_loading(Prepared); - {error, ModRsns} -> - Blacklist = + Blacklist = lists:foldr(fun({ModError, Error}, Acc) -> case Error of - %perhaps cover other cases of failure? + % perhaps cover other cases of failure? on_load_not_allowed -> reload_modules([ModError], false), [ModError|Acc]; - _ -> + _ -> ?DEBUG("Module ~p failed to atomic load because ~p", [ModError, Error]), [ModError|Acc] end @@ -151,16 +189,15 @@ reload_modules(Modules, true) -> ), reload_modules(Modules -- Blacklist, true) end; - -%% Older versions, use a more ad-hoc mechanism. reload_modules(Modules, false) -> + %% Older versions, use a more ad-hoc mechanism. lists:foreach(fun(M) -> - code:delete(M), - code:purge(M), + code:delete(M), + code:purge(M), case code:load_file(M) of {module, M} -> ok; {error, Error} -> ?DEBUG("Module ~p failed to load because ~p", [M, Error]) end end, Modules - ). \ No newline at end of file + ). diff --git a/src/rebar_api.erl b/src/rebar_api.erl index 6ebc500..9d9071e 100644 --- a/src/rebar_api.erl +++ b/src/rebar_api.erl @@ -1,4 +1,4 @@ -%%% Packages rebar.hrl features and macros into a more generic API +%%% @doc Packages rebar.hrl features and macros into a more generic API %%% that can be used by plugin builders. -module(rebar_api). -include("rebar.hrl"). @@ -30,42 +30,62 @@ abort() -> ?FAIL. abort(Str, Args) -> ?ABORT(Str, Args). %% @doc Prints to the console, including a newline +-spec console(string(), list()) -> ok. console(Str, Args) -> ?CONSOLE(Str, Args). %% @doc logs with severity `debug' +-spec debug(string(), list()) -> ok. debug(Str, Args) -> ?DEBUG(Str, Args). + %% @doc logs with severity `info' +-spec info(string(), list()) -> ok. info(Str, Args) -> ?INFO(Str, Args). + %% @doc logs with severity `warn' +-spec warn(string(), list()) -> ok. warn(Str, Args) -> ?WARN(Str, Args). + %% @doc logs with severity `error' +-spec error(string(), list()) -> ok. error(Str, Args) -> ?ERROR(Str, Args). -%% -%% Given env. variable FOO we want to expand all references to -%% it in InStr. References can have two forms: $FOO and ${FOO} -%% The end of form $FOO is delimited with whitespace or eol -%% +%% @doc Given env. variable `FOO' we want to expand all references to +%% it in `InStr'. References can have two forms: `$FOO' and `${FOO}' +%% The end of form `$FOO' is delimited with whitespace or EOL +-spec expand_env_variable(string(), string(), term()) -> string(). expand_env_variable(InStr, VarName, RawVarValue) -> rebar_utils:expand_env_variable(InStr, VarName, RawVarValue). +%% @doc returns the sytem architecture, in strings like +%% `"19.0.4-x86_64-unknown-linux-gnu-64"'. +-spec get_arch() -> string(). get_arch() -> rebar_utils:get_arch(). +%% @doc returns the size of a word on the system, as a string +-spec wordsize() -> string(). wordsize() -> rebar_utils:wordsize(). - -%% Add deps to the code path +%% @doc Add deps to the code path +-spec add_deps_to_path(rebar_state:t()) -> ok. add_deps_to_path(State) -> code:add_pathsa(rebar_state:code_paths(State, all_deps)). -%% Revert to only having the beams necessary for running rebar3 and plugins in the path +%% @doc Revert to only having the beams necessary for running rebar3 and +%% plugins in the path +-spec restore_code_path(rebar_state:t()) -> true | {error, term()}. restore_code_path(State) -> rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)). +%% @doc checks if the current working directory is the base directory +%% for the project. +-spec processing_base_dir(rebar_state:t()) -> boolean(). processing_base_dir(State) -> rebar_dir:processing_base_dir(State). +%% @doc returns the SSL options adequate for the project based on +%% its configuration, including for validation of certs. +-spec ssl_opts(string()) -> [term()]. ssl_opts(Url) -> rebar_pkg_resource:ssl_opts(Url). diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index 67acf54..acefdb4 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -1,3 +1,5 @@ +%%% @doc utility functions to do the basic discovery of apps +%%% and layout for the project. -module(rebar_app_discover). -export([do/2, @@ -11,6 +13,8 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). +%% @doc from the base directory, +-spec do(rebar_state:t(), [file:filename()]) -> rebar_state:t(). do(State, LibDirs) -> BaseDir = rebar_state:dir(State), Dirs = [filename:join(BaseDir, LibDir) || LibDir <- LibDirs], @@ -55,6 +59,10 @@ do(State, LibDirs) -> end end, State1, SortedApps). +%% @doc checks whether there is an app at the top level (and returns its +%% name) or the 'root' atom in case we're in an umbrella project. +-spec define_root_app([rebar_app_info:t()], rebar_state:t()) -> + root | binary(). define_root_app(Apps, State) -> RootDir = rebar_dir:root_dir(State), case ec_lists:find(fun(X) -> @@ -67,11 +75,17 @@ define_root_app(Apps, State) -> root end. +%% @doc formatting errors from the module. +-spec format_error(term()) -> iodata(). format_error({module_list, File}) -> io_lib:format("Error reading module list from ~p~n", [File]); format_error({missing_module, Module}) -> io_lib:format("Module defined in app file missing: ~p~n", [Module]). +%% @doc handles the merging and application of profiles and overrides +%% for a given application, within its own context. +-spec merge_deps(rebar_app_info:t(), rebar_state:t()) -> + {rebar_app_info:t(), rebar_state:t()}. merge_deps(AppInfo, State) -> %% These steps make sure that hooks and artifacts are run in the context of %% the application they are defined at. If an umbrella structure is used and @@ -97,6 +111,10 @@ merge_deps(AppInfo, State) -> {AppInfo2, State2}. +%% @doc Applies a given profile for an app, ensuring the deps +%% match the context it will require. +-spec handle_profile(atom(), binary(), rebar_app_info:t(), rebar_state:t()) -> + rebar_state:t(). handle_profile(Profile, Name, AppInfo, State) -> TopParsedDeps = rebar_state:get(State, {parsed_deps, Profile}, {[], []}), TopLevelProfileDeps = rebar_state:get(State, {deps, Profile}, []), @@ -113,6 +131,12 @@ handle_profile(Profile, Name, AppInfo, State) -> State2 = rebar_state:set(State1, {deps, Profile}, ProfileDeps2), rebar_state:set(State2, {parsed_deps, Profile}, TopParsedDeps++ParsedDeps). +%% @doc parses all the known dependencies for a given profile +-spec parse_profile_deps(Profile, Name, Deps, Opts, rebar_state:t()) -> [rebar_app_info:t()] when + Profile :: atom(), + Name :: binary(), + Deps :: [term()], % TODO: refine types + Opts :: term(). % TODO: refine types parse_profile_deps(Profile, Name, Deps, Opts, State) -> DepsDir = rebar_prv_install_deps:profile_dep_dir(State, Profile), Locks = rebar_state:get(State, {locks, Profile}, []), @@ -123,14 +147,21 @@ parse_profile_deps(Profile, Name, Deps, Opts, State) -> ,Locks ,1). +%% @doc Find the app-level config and return the state updated +%% with the relevant app-level data. +-spec project_app_config(rebar_app_info:t(), rebar_state:t()) -> + {Config, rebar_state:t()} when + Config :: [any()]. project_app_config(AppInfo, State) -> C = rebar_config:consult(rebar_app_info:dir(AppInfo)), Dir = rebar_app_info:dir(AppInfo), Opts = maybe_reset_hooks(Dir, rebar_state:opts(State), State), {C, rebar_state:opts(State, Opts)}. -%% Here we check if the app is at the root of the project. +%% @doc Here we check if the app is at the root of the project. %% If it is, then drop the hooks from the config so they aren't run twice +-spec maybe_reset_hooks(file:filename(), Opts, rebar_state:t()) -> Opts when + Opts :: rebar_dict(). maybe_reset_hooks(Dir, Opts, State) -> case ec_file:real_dir_path(rebar_dir:root_dir(State)) of Dir -> @@ -139,17 +170,22 @@ maybe_reset_hooks(Dir, Opts, State) -> Opts end. +%% @doc make the hooks empty for a given set of options +-spec reset_hooks(Opts) -> Opts when Opts :: rebar_dict(). reset_hooks(Opts) -> lists:foldl(fun(Key, OptsAcc) -> rebar_opts:set(OptsAcc, Key, []) end, Opts, [post_hooks, pre_hooks, provider_hooks, artifacts]). --spec all_app_dirs(list(file:name())) -> list(file:name()). +%% @doc find the directories for all apps +-spec all_app_dirs([file:name()]) -> [file:name()]. all_app_dirs(LibDirs) -> lists:flatmap(fun(LibDir) -> app_dirs(LibDir) end, LibDirs). +%% @doc find the directories based on the library directories +-spec app_dirs([file:name()]) -> [file:name()]. app_dirs(LibDir) -> Path1 = filename:join([LibDir, "src", @@ -168,32 +204,51 @@ app_dirs(LibDir) -> [app_dir(File) || File <- Files] ++ Acc end, [], [Path1, Path2, Path3])). +%% @doc find all apps that haven't been built in a list of directories +-spec find_unbuilt_apps([file:filename_all()]) -> [rebar_app_info:t()]. find_unbuilt_apps(LibDirs) -> find_apps(LibDirs, invalid). +%% @doc for each directory passed, find all apps that are valid. +%% Returns all the related app info records. -spec find_apps([file:filename_all()]) -> [rebar_app_info:t()]. find_apps(LibDirs) -> find_apps(LibDirs, valid). +%% @doc for each directory passed, find all apps according +%% to the validity rule passed in. Returns all the related +%% app info records. -spec find_apps([file:filename_all()], valid | invalid | all) -> [rebar_app_info:t()]. find_apps(LibDirs, Validate) -> rebar_utils:filtermap(fun(AppDir) -> find_app(AppDir, Validate) end, all_app_dirs(LibDirs)). +%% @doc check that a given app in a directory is there, and whether it's +%% valid or not based on the second argument. Returns the related +%% app info record. -spec find_app(file:filename_all(), valid | invalid | all) -> {true, rebar_app_info:t()} | false. find_app(AppDir, Validate) -> find_app(rebar_app_info:new(), AppDir, Validate). +%% @doc check that a given app in a directory is there, and whether it's +%% valid or not based on the second argument. Returns the related +%% app info record. +-spec find_app(rebar_app_info:t(), file:filename_all(), valid | invalid | all) -> + {true, rebar_app_info:t()} | false. find_app(AppInfo, AppDir, Validate) -> AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])), AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])), AppSrcScriptFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src.script"])), try_handle_app_file(AppInfo, AppFile, AppDir, AppSrcFile, AppSrcScriptFile, Validate). +%% @doc find the directory that an appfile has +-spec app_dir(file:filename()) -> file:filename(). app_dir(AppFile) -> filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))). +%% @doc populates an app info record based on an app directory and its +%% app file. -spec create_app_info(rebar_app_info:t(), file:name(), file:name()) -> rebar_app_info:t(). create_app_info(AppInfo, AppDir, AppFile) -> [{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile), @@ -215,8 +270,15 @@ create_app_info(AppInfo, AppDir, AppFile) -> end, rebar_app_info:dir(rebar_app_info:valid(AppInfo2, Valid), AppDir). -%% Read in and parse the .app file if it is availabe. Do the same for +%% @doc Read in and parse the .app file if it is availabe. Do the same for %% the .app.src file if it exists. +-spec try_handle_app_file(AppInfo, AppFile, AppDir, AppSrcFile, AppSrcScriptFile, valid | invalid | all) -> + {true, AppInfo} | false when + AppInfo :: rebar_app_info:t(), + AppFile :: file:filename(), + AppDir :: file:filename(), + AppSrcFile :: file:filename(), + AppSrcScriptFile :: file:filename(). try_handle_app_file(AppInfo, [], AppDir, [], AppSrcScriptFile, Validate) -> try_handle_app_src_file(AppInfo, [], AppDir, AppSrcScriptFile, Validate); try_handle_app_file(AppInfo, [], AppDir, AppSrcFile, _, Validate) -> @@ -260,7 +322,14 @@ try_handle_app_file(AppInfo0, [File], AppDir, AppSrcFile, _, Validate) -> try_handle_app_file(_AppInfo, Other, _AppDir, _AppSrcFile, _, _Validate) -> throw({error, {multiple_app_files, Other}}). -%% Read in the .app.src file if we aren't looking for a valid (already built) app +%% @doc Read in the .app.src file if we aren't looking for a valid (already +%% built) app. +-spec try_handle_app_src_file(AppInfo, AppFile, AppDir, AppSrcFile, valid | invalid | all) -> + {true, AppInfo} | false when + AppInfo :: rebar_app_info:t(), + AppFile :: file:filename(), + AppDir :: file:filename(), + AppSrcFile :: file:filename(). try_handle_app_src_file(_AppInfo, _, _AppDir, [], _Validate) -> false; try_handle_app_src_file(_AppInfo, _, _AppDir, _AppSrcFile, valid) -> @@ -277,9 +346,13 @@ try_handle_app_src_file(AppInfo, _, AppDir, [File], Validate) when Validate =:= try_handle_app_src_file(_AppInfo, _, _AppDir, Other, _Validate) -> throw({error, {multiple_app_files, Other}}). +%% @doc checks whether the given app is not blacklisted in the config. +-spec enable(rebar_state:t(), rebar_app_info:t()) -> boolean(). enable(State, AppInfo) -> not lists:member(to_atom(rebar_app_info:name(AppInfo)), rebar_state:get(State, excluded_apps, [])). +%% @private convert a binary to an atom. +-spec to_atom(binary()) -> atom(). to_atom(Bin) -> list_to_atom(binary_to_list(Bin)). diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index 4f19d77..fdaadb8 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -103,11 +103,13 @@ new() -> #app_info_t{}. +%% @doc Build a new app info value with only the app name set. -spec new(atom() | binary() | string()) -> {ok, t()}. new(AppName) -> {ok, #app_info_t{name=ec_cnv:to_binary(AppName)}}. +%% @doc Build a new app info value with only the name and version set. -spec new(atom() | binary() | string(), binary() | string()) -> {ok, t()}. new(AppName, Vsn) -> @@ -144,6 +146,9 @@ new(Parent, AppName, Vsn, Dir, Deps) -> out_dir=ec_cnv:to_list(Dir), deps=Deps}}. +%% @doc update the opts based on the contents of a config +%% file for the app +-spec update_opts(t(), rebar_dict(), [any()]) -> t(). update_opts(AppInfo, Opts, Config) -> LockDeps = case resource_type(AppInfo) of pkg -> @@ -163,6 +168,8 @@ update_opts(AppInfo, Opts, Config) -> AppInfo#app_info_t{opts=NewOpts ,default=NewOpts}. +%% @private extract the deps for an app in `Dir' based on its config file data +-spec deps_from_config(file:filename(), [any()]) -> [{tuple(), any()}, ...]. deps_from_config(Dir, Config) -> case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of [] -> @@ -184,30 +191,48 @@ discover(Dir) -> not_found end. +%% @doc get the name of the app. -spec name(t()) -> binary(). name(#app_info_t{name=Name}) -> Name. +%% @doc set the name of the app. -spec name(t(), atom() | binary() | string()) -> t(). name(AppInfo=#app_info_t{}, AppName) -> AppInfo#app_info_t{name=ec_cnv:to_binary(AppName)}. +%% @doc get the dictionary of options for the app. +-spec opts(t()) -> rebar_dict(). opts(#app_info_t{opts=Opts}) -> Opts. +%% @doc set the dictionary of options for the app. +-spec opts(t(), rebar_dict()) -> t(). opts(AppInfo, Opts) -> AppInfo#app_info_t{opts=Opts}. +%% @doc get the dictionary of options under the default profile. +%% Represents a root set prior to applying other profiles. +-spec default(t()) -> rebar_dict(). default(#app_info_t{default=Default}) -> Default. +%% @doc set the dictionary of options under the default profile. +%% Useful when re-applying profile. +-spec default(t(), rebar_dict()) -> t(). default(AppInfo, Default) -> AppInfo#app_info_t{default=Default}. +%% @doc look up a value in the dictionary of options; fails if +%% the key for it does not exist. +-spec get(t(), term()) -> term(). get(AppInfo, Key) -> {ok, Value} = dict:find(Key, AppInfo#app_info_t.opts), Value. +%% @doc look up a value in the dictionary of options; returns +%% a `Default' value otherwise. +-spec get(t(), term(), term()) -> term(). get(AppInfo, Key, Default) -> case dict:find(Key, AppInfo#app_info_t.opts) of {ok, Value} -> @@ -216,10 +241,12 @@ get(AppInfo, Key, Default) -> Default end. +%% @doc sets a given value in the dictionary of options for the app. -spec set(t(), any(), any()) -> t(). set(AppInfo=#app_info_t{opts=Opts}, Key, Value) -> AppInfo#app_info_t{opts = dict:store(Key, Value, Opts)}. +%% @doc finds the .app.src file for an app, if any. -spec app_file_src(t()) -> file:filename_all() | undefined. app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name}) -> AppFileSrc = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src"]), @@ -232,12 +259,15 @@ app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name}) -> app_file_src(#app_info_t{app_file_src=AppFileSrc}) -> ec_cnv:to_list(AppFileSrc). +%% @doc sets the .app.src file for an app. An app without such a file +%% can explicitly be set with `undefined'. -spec app_file_src(t(), file:filename_all() | undefined) -> t(). app_file_src(AppInfo=#app_info_t{}, undefined) -> AppInfo#app_info_t{app_file_src=undefined}; app_file_src(AppInfo=#app_info_t{}, AppFileSrc) -> AppInfo#app_info_t{app_file_src=ec_cnv:to_list(AppFileSrc)}. +%% @doc finds the .app.src.script file for an app, if any. -spec app_file_src_script(t()) -> file:filename_all() | undefined. app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Name}) -> AppFileSrcScript = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src.script"]), @@ -250,12 +280,15 @@ app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Nam app_file_src_script(#app_info_t{app_file_src_script=AppFileSrcScript}) -> ec_cnv:to_list(AppFileSrcScript). +%% @doc sets the .app.src.script file for an app. An app without such a file +%% can explicitly be set with `undefined'. -spec app_file_src_script(t(), file:filename_all()) -> t(). app_file_src_script(AppInfo=#app_info_t{}, undefined) -> AppInfo#app_info_t{app_file_src_script=undefined}; app_file_src_script(AppInfo=#app_info_t{}, AppFileSrcScript) -> AppInfo#app_info_t{app_file_src_script=ec_cnv:to_list(AppFileSrcScript)}. +%% @doc finds the .app file for an app, if any. -spec app_file(t()) -> file:filename_all() | undefined. app_file(#app_info_t{app_file=undefined, out_dir=Dir, name=Name}) -> AppFile = filename:join([ec_cnv:to_list(Dir), "ebin", ec_cnv:to_list(Name)++".app"]), @@ -268,10 +301,13 @@ app_file(#app_info_t{app_file=undefined, out_dir=Dir, name=Name}) -> app_file(#app_info_t{app_file=AppFile}) -> AppFile. +%% @doc sets the .app file for an app. -spec app_file(t(), file:filename_all()) -> t(). app_file(AppInfo=#app_info_t{}, AppFile) -> AppInfo#app_info_t{app_file=AppFile}. +%% @doc returns the information stored in the app's app file, +%% or if none, from the .app.src file. -spec app_details(t()) -> list(). app_details(AppInfo=#app_info_t{app_details=[]}) -> case app_file(AppInfo) of @@ -290,59 +326,83 @@ app_details(AppInfo=#app_info_t{app_details=[]}) -> app_details(#app_info_t{app_details=AppDetails}) -> AppDetails. +%% @doc stores the information that would be returned from the +%% app file, when reading from `app_details/1'. -spec app_details(t(), list()) -> t(). app_details(AppInfo=#app_info_t{}, AppDetails) -> AppInfo#app_info_t{app_details=AppDetails}. +%% @doc returns the app's parent in the dep tree. +-spec parent(t()) -> root | binary(). parent(#app_info_t{parent=Parent}) -> Parent. +%% @doc sets the app's parent. -spec parent(t(), binary() | root) -> t(). parent(AppInfo=#app_info_t{}, Parent) -> AppInfo#app_info_t{parent=Parent}. +%% @doc returns the original version of the app (unevaluated if +%% asking for a semver) -spec original_vsn(t()) -> string(). original_vsn(#app_info_t{original_vsn=Vsn}) -> Vsn. +%% @doc stores the original version of the app (unevaluated if +%% asking for a semver) -spec original_vsn(t(), string()) -> t(). original_vsn(AppInfo=#app_info_t{}, Vsn) -> AppInfo#app_info_t{original_vsn=Vsn}. +%% @doc returns the list of applications the app depends on. -spec applications(t()) -> list(). applications(#app_info_t{applications=Applications}) -> Applications. +%% @doc sets the list of applications the app depends on. +%% Should be obtained from the app file. -spec applications(t(), list()) -> t(). applications(AppInfo=#app_info_t{}, Applications) -> AppInfo#app_info_t{applications=Applications}. +%% @doc returns the list of active profiles -spec profiles(t()) -> list(). profiles(#app_info_t{profiles=Profiles}) -> Profiles. +%% @doc sets the list of active profiles -spec profiles(t(), list()) -> t(). profiles(AppInfo=#app_info_t{}, Profiles) -> AppInfo#app_info_t{profiles=Profiles}. +%% @doc returns the list of dependencies -spec deps(t()) -> list(). deps(#app_info_t{deps=Deps}) -> Deps. +%% @doc sets the list of dependencies. -spec deps(t(), list()) -> t(). deps(AppInfo=#app_info_t{}, Deps) -> AppInfo#app_info_t{deps=Deps}. -dep_level(AppInfo=#app_info_t{}, Level) -> - AppInfo#app_info_t{dep_level=Level}. - +%% @doc returns the level the app has in the lock files or in the +%% dep tree. +-spec dep_level(t()) -> non_neg_integer(). dep_level(#app_info_t{dep_level=Level}) -> Level. +%% @doc sets the level the app has in the lock files or in the +%% dep tree. +-spec dep_level(t(), non_neg_integer()) -> t(). +dep_level(AppInfo=#app_info_t{}, Level) -> + AppInfo#app_info_t{dep_level=Level}. + +%% @doc returns the directory that contains the app. -spec dir(t()) -> file:name(). dir(#app_info_t{dir=Dir}) -> Dir. +%% @doc sets the directory that contains the app. -spec dir(t(), file:name()) -> t(). dir(AppInfo=#app_info_t{out_dir=undefined}, Dir) -> AppInfo#app_info_t{dir=ec_cnv:to_list(Dir), @@ -350,54 +410,69 @@ dir(AppInfo=#app_info_t{out_dir=undefined}, Dir) -> dir(AppInfo=#app_info_t{}, Dir) -> AppInfo#app_info_t{dir=ec_cnv:to_list(Dir)}. +%% @doc returns the directory where build artifacts for the app +%% should go -spec out_dir(t()) -> file:name(). out_dir(#app_info_t{out_dir=OutDir}) -> OutDir. +%% @doc sets the directory where build artifacts for the app +%% should go -spec out_dir(t(), file:name()) -> t(). out_dir(AppInfo=#app_info_t{}, OutDir) -> AppInfo#app_info_t{out_dir=ec_cnv:to_list(OutDir)}. +%% @doc gets the directory where ebin files for the app should go -spec ebin_dir(t()) -> file:name(). ebin_dir(#app_info_t{out_dir=OutDir}) -> ec_cnv:to_list(filename:join(OutDir, "ebin")). +%% @doc gets the directory where private files for the app should go -spec priv_dir(t()) -> file:name(). priv_dir(#app_info_t{out_dir=OutDir}) -> ec_cnv:to_list(filename:join(OutDir, "priv")). --spec resource_type(t(), pkg | src) -> t(). -resource_type(AppInfo=#app_info_t{}, Type) -> - AppInfo#app_info_t{resource_type=Type}. - +%% @doc returns whether the app is source app or a package app. -spec resource_type(t()) -> pkg | src. resource_type(#app_info_t{resource_type=ResourceType}) -> ResourceType. --spec source(t(), string() | tuple() | checkout) -> t(). -source(AppInfo=#app_info_t{}, Source) -> - AppInfo#app_info_t{source=Source}. +%% @doc sets whether the app is source app or a package app. +-spec resource_type(t(), pkg | src) -> t(). +resource_type(AppInfo=#app_info_t{}, Type) -> + AppInfo#app_info_t{resource_type=Type}. +%% @doc finds the source specification for the app -spec source(t()) -> string() | tuple(). source(#app_info_t{source=Source}) -> Source. --spec is_lock(t(), boolean()) -> t(). -is_lock(AppInfo=#app_info_t{}, IsLock) -> - AppInfo#app_info_t{is_lock=IsLock}. +%% @doc sets the source specification for the app +-spec source(t(), string() | tuple() | checkout) -> t(). +source(AppInfo=#app_info_t{}, Source) -> + AppInfo#app_info_t{source=Source}. +%% @doc returns the lock status for the app -spec is_lock(t()) -> boolean(). is_lock(#app_info_t{is_lock=IsLock}) -> IsLock. --spec is_checkout(t(), boolean()) -> t(). -is_checkout(AppInfo=#app_info_t{}, IsCheckout) -> - AppInfo#app_info_t{is_checkout=IsCheckout}. +%% @doc sets the lock status for the app +-spec is_lock(t(), boolean()) -> t(). +is_lock(AppInfo=#app_info_t{}, IsLock) -> + AppInfo#app_info_t{is_lock=IsLock}. +%% @doc returns whether the app is a checkout app or not -spec is_checkout(t()) -> boolean(). is_checkout(#app_info_t{is_checkout=IsCheckout}) -> IsCheckout. +%% @doc sets whether the app is a checkout app or not +-spec is_checkout(t(), boolean()) -> t(). +is_checkout(AppInfo=#app_info_t{}, IsCheckout) -> + AppInfo#app_info_t{is_checkout=IsCheckout}. + +%% @doc returns whether the app is valid (built) or not -spec valid(t()) -> boolean(). valid(AppInfo=#app_info_t{valid=undefined}) -> case rebar_app_utils:validate_application_info(AppInfo) =:= true @@ -410,14 +485,22 @@ valid(AppInfo=#app_info_t{valid=undefined}) -> valid(#app_info_t{valid=Valid}) -> Valid. +%% @doc sets whether the app is valid (built) or not. If left unset, +%% rebar3 will do the detection of the status itself. -spec valid(t(), boolean()) -> t(). valid(AppInfo=#app_info_t{}, Valid) -> AppInfo#app_info_t{valid=Valid}. +%% @doc checks whether the app can be built with the current +%% Erlang/OTP version. If the check fails, the function raises +%% an exception and displays an error. +-spec verify_otp_vsn(t()) -> ok | no_return(). verify_otp_vsn(AppInfo) -> rebar_utils:check_min_otp_version(rebar_app_info:get(AppInfo, minimum_otp_vsn, undefined)), rebar_utils:check_blacklisted_otp_versions(rebar_app_info:get(AppInfo, blacklisted_otp_vsns, [])). +%% @doc checks whether all the build artifacts for an app to be considered +%% valid are present. -spec has_all_artifacts(#app_info_t{}) -> true | {false, file:filename()}. has_all_artifacts(AppInfo) -> Artifacts = rebar_app_info:get(AppInfo, artifacts, []), @@ -427,6 +510,10 @@ has_all_artifacts(AppInfo) -> ,{out_dir, OutDir}], all(OutDir, Context, Artifacts). +%% @private checks that all files/artifacts in the directory are found. +%% Template evaluation must happen and a bbmustache context needs to +%% be provided. +-spec all(file:filename(), term(), [string()]) -> true | {false, string()}. all(_, _, []) -> true; all(Dir, Context, [File|Artifacts]) -> @@ -441,15 +528,23 @@ all(Dir, Context, [File|Artifacts]) -> %%%%% +%% @doc given a set of override rules, modify the app info accordingly +-spec apply_overrides(list(), t()) -> t(). apply_overrides(Overrides, AppInfo) -> Name = binary_to_atom(rebar_app_info:name(AppInfo), utf8), Opts = rebar_opts:apply_overrides(opts(AppInfo), Name, Overrides), AppInfo#app_info_t{default=Opts, opts=Opts}. +%% @doc adds a new profile with its own config to the app data +-spec add_to_profile(t(), atom(), [{_,_}]) -> t(). add_to_profile(AppInfo, Profile, KVs) when is_atom(Profile), is_list(KVs) -> Opts = rebar_opts:add_to_profile(opts(AppInfo), Profile, KVs), AppInfo#app_info_t{opts=Opts}. +%% @doc applies and merges the profile configuration in the specified order +%% of profiles (or for a single profile) and returns an app info record +%% with the resulting configuration +-spec apply_profiles(t(), atom() | [atom(),...]) -> t(). apply_profiles(AppInfo, Profile) when not is_list(Profile) -> apply_profiles(AppInfo, [Profile]); apply_profiles(AppInfo, [default]) -> @@ -481,9 +576,13 @@ apply_profiles(AppInfo=#app_info_t{default = Defaults, profiles=CurrentProfiles} end, Defaults, AppliedProfiles), AppInfo#app_info_t{profiles = AppliedProfiles, opts=NewOpts}. +%% @private drops duplicated profile definitions +-spec deduplicate(list()) -> list(). deduplicate(Profiles) -> do_deduplicate(lists:reverse(Profiles), []). +%% @private drops duplicated profile definitions +-spec do_deduplicate(list(), list()) -> list(). do_deduplicate([], Acc) -> Acc; do_deduplicate([Head | Rest], Acc) -> diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index 847b0a5..7154999 100644 --- a/src/rebar_app_utils.erl +++ b/src/rebar_app_utils.erl @@ -44,10 +44,14 @@ %% Public API %% =================================================================== +%% @doc finds the proper app info record for a given app name in a list of +%% such records. -spec find(binary(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error. find(Name, Apps) -> ec_lists:find(fun(App) -> rebar_app_info:name(App) =:= Name end, Apps). +%% @doc finds the proper app info record for a given app name at a given version +%% in a list of such records. -spec find(binary(), binary(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error. find(Name, Vsn, Apps) -> ec_lists:find(fun(App) -> @@ -55,11 +59,18 @@ find(Name, Vsn, Apps) -> andalso rebar_app_info:original_vsn(App) =:= Vsn end, Apps). +%% @doc checks if a given file is .app.src file is_app_src(Filename) -> %% If removing the extension .app.src yields a shorter name, %% this is an .app.src file. Filename =/= filename:rootname(Filename, ".app.src"). +%% @doc translates the name of the .app.src[.script] file to where +%% its .app counterpart should be stored. +-spec app_src_to_app(OutDir, SrcFilename) -> OutFilename when + OutDir :: file:filename(), + SrcFilename :: file:filename(), + OutFilename :: file:filename(). app_src_to_app(OutDir, Filename) -> AppFile = case lists:suffix(".app.src", Filename) of @@ -72,10 +83,16 @@ app_src_to_app(OutDir, Filename) -> filelib:ensure_dir(AppFile), AppFile. +%% @doc checks whether the .app file has all the required data to be valid, +%% and cross-references it with compiled modules on disk -spec validate_application_info(rebar_app_info:t()) -> boolean(). validate_application_info(AppInfo) -> validate_application_info(AppInfo, rebar_app_info:app_details(AppInfo)). +%% @doc checks whether the .app file has all the required data to be valid +%% and cross-references it with compiled modules on disk. +%% The app info is passed explicitly as a second argument. +-spec validate_application_info(rebar_app_info:t(), list()) -> boolean(). validate_application_info(AppInfo, AppDetail) -> EbinDir = rebar_app_info:ebin_dir(AppInfo), case rebar_app_info:app_file(AppInfo) of @@ -90,13 +107,37 @@ validate_application_info(AppInfo, AppDetail) -> end end. --spec parse_deps(binary(), list(), rebar_state:t(), list(), integer()) -> [rebar_app_info:t()]. +%% @doc parses all dependencies from the root of the project +-spec parse_deps(Dir, Deps, State, Locks, Level) -> [rebar_app_info:t()] when + Dir :: file:filename(), + Deps :: [tuple() | atom() | binary()], % TODO: meta to source() | lock() + State :: rebar_state:t(), + Locks :: [tuple()], % TODO: meta to [lock()] + Level :: non_neg_integer(). parse_deps(DepsDir, Deps, State, Locks, Level) -> parse_deps(root, DepsDir, Deps, State, Locks, Level). +%% @doc runs `parse_dep/6' for a set of dependencies. +-spec parse_deps(Parent, Dir, Deps, State, Locks, Level) -> [rebar_app_info:t()] when + Parent :: root | binary(), + Dir :: file:filename(), + Deps :: [tuple() | atom() | binary()], % TODO: meta to source() | lock() + State :: rebar_state:t(), + Locks :: [tuple()], % TODO: meta to [lock()] + Level :: non_neg_integer(). parse_deps(Parent, DepsDir, Deps, State, Locks, Level) -> [parse_dep(Dep, Parent, DepsDir, State, Locks, Level) || Dep <- Deps]. +%% @doc for a given dep, return its app info record. The function +%% also has to choose whether to define the dep from its immediate spec +%% (if it is a newer thing) or from the locks specified in the lockfile. +-spec parse_dep(Dep, Parent, Dir, State, Locks, Level) -> rebar_app_info:t() when + Dep :: tuple() | atom() | binary(), % TODO: meta to source() | lock() + Parent :: root | binary(), + Dir :: file:filename(), + State :: rebar_state:t(), + Locks :: [tuple()], % TODO: meta to [lock()] + Level :: non_neg_integer(). parse_dep(Dep, Parent, DepsDir, State, Locks, Level) -> Name = case Dep of Dep when is_tuple(Dep) -> @@ -117,6 +158,14 @@ parse_dep(Dep, Parent, DepsDir, State, Locks, Level) -> end end. +%% @doc converts a dependency definition and a location for it on disk +%% into an app info tuple representing it. +-spec parse_dep(Parent, Dep, Dir, IsLock, State) -> rebar_app_info:t() when + Parent :: root | binary(), + Dep :: tuple() | atom() | binary(), % TODO: meta to source() | lock() + Dir :: file:filename(), + IsLock :: boolean(), + State :: rebar_state:t(). parse_dep(Parent, {Name, Vsn, {pkg, PkgName}}, DepsDir, IsLock, State) -> {PkgName1, PkgVsn} = {ec_cnv:to_binary(PkgName), ec_cnv:to_binary(Vsn)}, dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn, undefined}, IsLock, State); @@ -152,6 +201,16 @@ parse_dep(Parent, {Name, Source, Level}, DepsDir, IsLock, State) when is_tuple(S parse_dep(_, Dep, _, _, _) -> throw(?PRV_ERROR({parse_dep, Dep})). +%% @doc convert a dependency that has just been fetched into +%% an app info record related to it +-spec dep_to_app(Parent, Dir, Name, Vsn, Source, IsLock, State) -> rebar_app_info:t() when + Parent :: root | binary(), + Dir :: file:filename(), + Name :: binary(), + Vsn :: iodata() | undefined, + Source :: tuple(), + IsLock :: boolean(), + State :: rebar_state:t(). dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), AppInfo = case rebar_app_info:discover(CheckoutsDir) of @@ -166,7 +225,7 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> not_found -> rebar_app_info:new(Parent, Name, Vsn, Dir, []) end, - update_source(AppInfo0, Source, State) + update_source(AppInfo0, Source, State) end, C = rebar_config:consult(rebar_app_info:dir(AppInfo)), AppInfo1 = rebar_app_info:update_opts(AppInfo, rebar_app_info:opts(AppInfo), C), @@ -177,6 +236,11 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> AppInfo5 = rebar_app_info:profiles(AppInfo4, [default]), rebar_app_info:is_lock(AppInfo5, IsLock). +%% @doc sets the source for a given dependency or app along with metadata +%% around version if required. +-spec update_source(rebar_app_info:t(), Source, rebar_state:t()) -> + rebar_app_info:t() when + Source :: tuple() | atom() | binary(). % TODO: meta to source() update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> {PkgName1, PkgVsn1} = case PkgVsn of undefined -> @@ -203,6 +267,9 @@ update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) -> update_source(AppInfo, Source, _State) -> rebar_app_info:source(AppInfo, Source). +%% @doc grab the checksum for a given package +-spec fetch_checksum(atom(), string(), iodata() | undefined, rebar_state:t()) -> + iodata() | no_return(). fetch_checksum(PkgName, PkgVsn, Hash, State) -> try rebar_packages:registry_checksum({pkg, PkgName, PkgVsn, Hash}, State) @@ -213,6 +280,8 @@ fetch_checksum(PkgName, PkgVsn, Hash, State) -> rebar_packages:registry_checksum({pkg, PkgName, PkgVsn, Hash}, State) end. +%% @doc convert a given exception's payload into an io description. +-spec format_error(any()) -> iolist(). format_error({missing_package, Package}) -> io_lib:format("Package not found in registry: ~s", [Package]); format_error({parse_dep, Dep}) -> @@ -224,6 +293,10 @@ format_error(Error) -> %% Internal functions %% =================================================================== +%% @private find the correct version of a package based on the version +%% and name passed in. +-spec get_package(binary(), binary() | string(), rebar_state:t()) -> + term() | no_return(). get_package(Dep, Vsn, State) -> case rebar_packages:find_highest_matching(Dep, Vsn, ?PACKAGE_TABLE, State) of {ok, HighestDepVsn} -> @@ -232,6 +305,8 @@ get_package(Dep, Vsn, State) -> throw(?PRV_ERROR({missing_package, ec_cnv:to_binary(Dep)})) end. +%% @private checks that all the beam files have been properly +%% created. -spec has_all_beams(file:filename_all(), [module()]) -> true | ?PRV_ERROR({missing_module, module()}). has_all_beams(EbinDir, [Module | ModuleList]) -> diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 5d54057..02d567c 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -36,21 +36,65 @@ format_error_source/2]). -define(DEFAULT_COMPILER_SOURCE_FORMAT, relative). +-type desc() :: term(). +-type loc() :: {line(), col()} | line(). +-type line() :: integer(). +-type col() :: integer(). +-type err_or_warn() :: {module(), desc()} | {loc(), module(), desc()}. + +-type compile_fn_ret() :: ok | {ok, [string()]} | skipped | term(). +-type compile_fn() :: fun((file:filename(), [{_,_}] | rebar_dict()) -> compile_fn_ret()). +-type compile_fn3() :: fun((file:filename(), file:filename(), [{_,_}] | rebar_dict()) + -> compile_fn_ret()). +-type error_tuple() :: {error, [string()], [string()]}. +-export_type([compile_fn/0, compile_fn_ret/0, error_tuple/0]). %% =================================================================== %% Public API %% =================================================================== +%% @doc Runs a compile job, applying `compile_fn()' to all files, +%% starting with `First' files, and then `RestFiles'. +-spec run(rebar_dict() | [{_,_}] , [First], [Next], compile_fn()) -> + compile_fn_ret() when + First :: file:filename(), + Next :: file:filename(). run(Config, FirstFiles, RestFiles, CompileFn) -> %% Compile the first files in sequence compile_each(FirstFiles++RestFiles, Config, CompileFn). +%% @doc Runs a compile job, applying `compile_fn3()' to all files, +%% starting with `First' files, and then the other content of `SourceDir'. +%% Files looked for are those ending in `SourceExt'. Results of the +%% compilation are put in `TargetDir' with the base file names +%% postfixed with `SourceExt'. +-spec run(rebar_dict() | [{_,_}] , [First], SourceDir, SourceExt, + TargetDir, TargetExt, compile_fn3()) -> compile_fn_ret() when + First :: file:filename(), + SourceDir :: file:filename(), + TargetDir :: file:filename(), + SourceExt :: string(), + TargetExt :: string(). run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn) -> run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, [check_last_mod]). +%% @doc Runs a compile job, applying `compile_fn3()' to all files, +%% starting with `First' files, and then the other content of `SourceDir'. +%% Files looked for are those ending in `SourceExt'. Results of the +%% compilation are put in `TargetDir' with the base file names +%% postfixed with `SourceExt'. +%% Additional compile options can be passed in the last argument as +%% a proplist. +-spec run(rebar_dict() | [{_,_}] , [First], SourceDir, SourceExt, + TargetDir, TargetExt, compile_fn3(), [term()]) -> compile_fn_ret() when + First :: file:filename(), + SourceDir :: file:filename(), + TargetDir :: file:filename(), + SourceExt :: string(), + TargetExt :: string(). run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, Opts) -> %% Convert simple extension to proper regex @@ -73,13 +117,26 @@ run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, simple_compile_wrapper(S, Target, Compile3Fn, C, CheckLastMod) end). +%% @doc Format good compiler results with warnings to work with +%% module internals. Assumes that warnings are not treated as errors. +-spec ok_tuple(file:filename(), [string()]) -> {ok, [string()]}. ok_tuple(Source, Ws) -> {ok, format_warnings(Source, Ws)}. +%% @doc format error and warning strings for a given source file +%% according to user preferences. +-spec error_tuple(file:filename(), [Err], [Warn], rebar_dict() | [{_,_}]) -> + error_tuple() when + Err :: string(), + Warn :: string(). error_tuple(Source, Es, Ws, Opts) -> {error, format_errors(Source, Es), format_warnings(Source, Ws, Opts)}. +%% @doc from a given path, and based on the user-provided options, +%% format the file path according to the preferences. +-spec format_error_source(file:filename(), rebar_dict() | [{_,_}]) -> + file:filename(). format_error_source(Path, Opts) -> Type = case rebar_opts:get(Opts, compiler_source_format, ?DEFAULT_COMPILER_SOURCE_FORMAT) of @@ -98,6 +155,8 @@ format_error_source(Path, Opts) -> rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd) end. +%% @private takes a filename and canonicalizes its path if it is a link. +-spec resolve_linked_source(file:filename()) -> file:filename(). resolve_linked_source(Src) -> {Dir, Base} = rebar_file_utils:split_dirname(Src), filename:join(rebar_file_utils:resolve_link(Dir), Base). @@ -106,6 +165,11 @@ resolve_linked_source(Src) -> %% Internal functions %% =================================================================== +%% @private if a check for last modifications is required, do the verification +%% and possibly skip the compile job. +-spec simple_compile_wrapper(Source, Target, compile_fn3(), [{_,_}] | rebar_dict(), boolean()) -> compile_fn_ret() when + Source :: file:filename(), + Target :: file:filename(). simple_compile_wrapper(Source, Target, Compile3Fn, Config, false) -> Compile3Fn(Source, Target, Config); simple_compile_wrapper(Source, Target, Compile3Fn, Config, true) -> @@ -116,18 +180,42 @@ simple_compile_wrapper(Source, Target, Compile3Fn, Config, true) -> skipped end. +%% @private take a basic source set of file fragments and a target location, +%% create a file path and name for a compile artifact. +-spec target_file(SourceFile, SourceDir, SourceExt, TargetDir, TargetExt) -> File when + SourceFile :: file:filename(), + SourceDir :: file:filename(), + TargetDir :: file:filename(), + SourceExt :: string(), + TargetExt :: string(), + File :: file:filename(). target_file(SourceFile, SourceDir, SourceExt, TargetDir, TargetExt) -> BaseFile = remove_common_path(SourceFile, SourceDir), filename:join([TargetDir, filename:basename(BaseFile, SourceExt) ++ TargetExt]). +%% @private removes the common prefix between two file paths. +%% The remainder of the first file path passed will have its ending returned +%% when either path starts diverging. +-spec remove_common_path(file:filename(), file:filename()) -> file:filename(). remove_common_path(Fname, Path) -> remove_common_path1(filename:split(Fname), filename:split(Path)). +%% @private given two lists of file fragments, discard the identical +%% prefixed sections, and return the final bit of the first operand +%% as a filename. +-spec remove_common_path1([string()], [string()]) -> file:filename(). remove_common_path1([Part | RestFilename], [Part | RestPath]) -> remove_common_path1(RestFilename, RestPath); remove_common_path1(FilenameParts, _) -> filename:join(FilenameParts). +%% @private runs the compile function `CompileFn' on every file +%% passed internally, along with the related project configuration. +%% If any errors are encountered, they're reported to stdout. +-spec compile_each([file:filename()], Config, CompileFn) -> Ret | no_return() when + Config :: [{_,_}] | rebar_dict(), + CompileFn :: compile_fn(), + Ret :: compile_fn_ret(). compile_each([], _Config, _CompileFn) -> ok; compile_each([Source | Rest], Config, CompileFn) -> @@ -148,12 +236,19 @@ compile_each([Source | Rest], Config, CompileFn) -> end, compile_each(Rest, Config, CompileFn). +%% @private Formats and returns errors ready to be output. +-spec format_errors(string(), [err_or_warn()]) -> [string()]. format_errors(Source, Errors) -> format_errors(Source, "", Errors). +%% @private Formats and returns warning strings ready to be output. +-spec format_warnings(string(), [err_or_warn()]) -> [string()]. format_warnings(Source, Warnings) -> format_warnings(Source, Warnings, []). +%% @private Formats and returns warnings; chooses the distinct format they +%% may have based on whether `warnings_as_errors' option is on. +-spec format_warnings(string(), [err_or_warn()], rebar_dict() | [{_,_}]) -> [string()]. format_warnings(Source, Warnings, Opts) -> %% `Opts' can be passed in both as a list or a dictionary depending %% on whether the first call to rebar_erlc_compiler was done with @@ -167,6 +262,11 @@ format_warnings(Source, Warnings, Opts) -> end, format_errors(Source, Prefix, Warnings). +%% @private output compiler errors if they're judged to be reportable. +-spec maybe_report(Reportable | term()) -> ok when + Reportable :: {{error, error_tuple()}, Source} | error_tuple() | ErrProps, + ErrProps :: [{error, string()} | Source, ...], + Source :: {source, string()}. maybe_report({{error, {error, _Es, _Ws}=ErrorsAndWarnings}, {source, _}}) -> maybe_report(ErrorsAndWarnings); maybe_report([{error, E}, {source, S}]) -> @@ -177,15 +277,23 @@ maybe_report({error, Es, Ws}) -> maybe_report(_) -> ok. +%% @private Outputs a bunch of strings, including a newline +-spec report([string()]) -> ok. report(Messages) -> lists:foreach(fun(Msg) -> io:format("~s~n", [Msg]) end, Messages). +%% private format compiler errors into proper outputtable strings +-spec format_errors(_, Extra, [err_or_warn()]) -> [string()] when + Extra :: string(). format_errors(_MainSource, Extra, Errors) -> [begin [format_error(Source, Extra, Desc) || Desc <- Descs] end || {Source, Descs} <- Errors]. +%% @private format compiler errors into proper outputtable strings +-spec format_error(file:filename(), Extra, err_or_warn()) -> string() when + Extra :: string(). format_error(Source, Extra, {{Line, Column}, Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), ?FMT("~s:~w:~w: ~s~s~n", [Source, Line, Column, Extra, ErrorDesc]); diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 5a35b87..84f40d4 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -46,17 +46,25 @@ %% Public API %% =================================================================== +%% @doc reads the default config file. -spec consult() -> [any()]. consult() -> consult_file(config_file()). +%% @doc reads the default config file in a given directory. -spec consult(file:name()) -> [any()]. consult(Dir) -> consult_file(filename:join(Dir, config_file())). +%% @doc reads a given app file, including the `.script' variations, +%% if any can be found. +-spec consult_app_file(file:filename()) -> [any()]. consult_app_file(File) -> consult_file_(File). +%% @doc reads the lock file for the project, and re-formats its +%% content to match the internals for rebar3. +-spec consult_lock_file(file:filename()) -> [any()]. % TODO: refine lock() consult_lock_file(File) -> Terms = consult_file_(File), case Terms of @@ -80,6 +88,11 @@ consult_lock_file(File) -> read_attrs(Vsn, Locks, Attrs) end. +%% @private outputs a warning for a newer lockfile format than supported +%% at most once. +%% The warning can also be cancelled by configuring the `warn_config_vsn' +%% OTP env variable. +-spec warn_vsn_once() -> ok. warn_vsn_once() -> Warn = application:get_env(rebar, warn_config_vsn) =/= {ok, false}, application:set_env(rebar, warn_config_vsn, false), @@ -93,6 +106,11 @@ warn_vsn_once() -> end. +%% @doc Converts the internal format for locks into the multi-version +%% compatible one used within rebar3 lock files. +%% @end +%% TODO: refine type for lock() +-spec write_lock_file(file:filename(), [any()]) -> ok | {error, term()}. write_lock_file(LockFile, Locks) -> {NewLocks, Attrs} = write_attrs(Locks), %% Write locks in the beta format, at least until it's been long @@ -107,27 +125,41 @@ write_lock_file(LockFile, Locks) -> format_attrs(Attrs)])) end. -%% Attributes have a special formatting to ensure there's only one per +%% @private Attributes have a special formatting to ensure there's only one per %% line in terms of pkg_hash, so we disturb source diffing as little %% as possible. +-spec format_attrs([term()]) -> iodata(). format_attrs([]) -> []; format_attrs([{pkg_hash, Vals}|T]) -> [io_lib:format("{pkg_hash,[~n",[]), format_hashes(Vals), "]}", maybe_comma(T) | format_attrs(T)]. +%% @private format hashing in order to disturb source diffing as little +%% as possible +-spec format_hashes([term()]) -> iodata(). format_hashes([]) -> []; format_hashes([{Pkg,Hash}|T]) -> [" {", io_lib:format("~p",[Pkg]), ", ", io_lib:format("~p", [Hash]), "}", maybe_comma(T) | format_hashes(T)]. +%% @private add a comma if we're not done with the full list of terms +%% to convert. +-spec maybe_comma([term()]) -> iodata(). maybe_comma([]) -> ""; maybe_comma([_|_]) -> io_lib:format(",~n", []). +%% @private extract attributes from the lock file and integrate them +%% into the full-blow internal lock format +%% @end +%% TODO: refine typings for lock() +-spec read_attrs(_, [any()], [any()]) -> [any()]. read_attrs(_Vsn, Locks, Attrs) -> %% Beta copy does not know how to expand attributes, but %% is ready to support it. expand_locks(Locks, extract_pkg_hashes(Attrs)). +%% @private extract the package hashes from lockfile attributes, if any. +-spec extract_pkg_hashes(list()) -> [binary()]. extract_pkg_hashes(Attrs) -> Props = case Attrs of [First|_] -> First; @@ -135,6 +167,11 @@ extract_pkg_hashes(Attrs) -> end, proplists:get_value(pkg_hash, Props, []). +%% @private extract attributes from the lock file and integrate them +%% into the full-blow internal lock format +%% @end +%% TODO: refine typings for lock() +-spec expand_locks(list(), list()) -> list(). expand_locks([], _Hashes) -> []; expand_locks([{Name, {pkg,PkgName,Vsn}, Lvl} | Locks], Hashes) -> @@ -143,6 +180,9 @@ expand_locks([{Name, {pkg,PkgName,Vsn}, Lvl} | Locks], Hashes) -> expand_locks([Lock|Locks], Hashes) -> [Lock | expand_locks(Locks, Hashes)]. +%% @private split up extra attributes for locks out of the internal lock +%% structure for backwards compatibility reasons +-spec write_attrs(list()) -> {list(), list()}. write_attrs(Locks) -> %% No attribute known that needs to be taken out of the structure, %% just return terms as is. @@ -152,6 +192,9 @@ write_attrs(Locks) -> _ -> {NewLocks, [{pkg_hash, lists:sort(Hashes)}]} end. +%% @private split up extra attributes for locks out of the internal lock +%% structure for backwards compatibility reasons +-spec split_locks(list(), list(), [{_,binary()}]) -> {list(), list()}. split_locks([], Locks, Hashes) -> {lists:reverse(Locks), Hashes}; split_locks([{Name, {pkg,PkgName,Vsn,undefined}, Lvl} | Locks], LAcc, HAcc) -> @@ -161,11 +204,17 @@ split_locks([{Name, {pkg,PkgName,Vsn,Hash}, Lvl} | Locks], LAcc, HAcc) -> split_locks([Lock|Locks], LAcc, HAcc) -> split_locks(Locks, [Lock|LAcc], HAcc). +%% @doc reads a given config file, including the `.script' variations, +%% if any can be found, and asserts that the config format is in +%% a key-value format. +-spec consult_file(file:filename()) -> [{_,_}]. consult_file(File) -> Terms = consult_file_(File), true = verify_config_format(Terms), Terms. +%% @private reads a given file; if the file has a `.script'-postfixed +%% counterpart, it is evaluated along with the original file. -spec consult_file_(file:name()) -> [any()]. consult_file_(File) when is_binary(File) -> consult_file_(binary_to_list(File)); @@ -185,6 +234,9 @@ consult_file_(File) -> end end. +%% @private checks that a list is in a key-value format. +%% Raises an exception in any other case. +-spec verify_config_format([{_,_}]) -> true. verify_config_format([]) -> true; verify_config_format([{_Key, _Value} | T]) -> @@ -192,11 +244,14 @@ verify_config_format([{_Key, _Value} | T]) -> verify_config_format([Term | _]) -> throw(?PRV_ERROR({bad_config_format, Term})). -%% no lockfile +%% @doc takes an existing configuration and the content of a lockfile +%% and merges the locks into the config. +-spec merge_locks([{_,_}], list()) -> [{_,_}]. merge_locks(Config, []) -> +%% no lockfile Config; -%% lockfile with entries merge_locks(Config, Locks) -> + %% lockfile with entries ConfigDeps = proplists:get_value(deps, Config, []), %% We want the top level deps only from the lock file. %% This ensures deterministic overrides for configs. @@ -206,6 +261,8 @@ merge_locks(Config, Locks) -> NewDeps = find_newly_added(ConfigDeps, Locks), [{{locks, default}, Locks}, {{deps, default}, NewDeps++Deps} | Config]. +%% @doc convert a given exception's payload into an io description. +-spec format_error(any()) -> iolist(). format_error({bad_config_format, Term}) -> io_lib:format("Unable to parse config. Term is not in {Key, Value} format:~n~p", [Term]); format_error({bad_dep_name, Dep}) -> @@ -215,6 +272,8 @@ format_error({bad_dep_name, Dep}) -> %% Internal functions %% =================================================================== +%% @private consults a consult file, then executes its related script file +%% with the data returned from the consult. -spec consult_and_eval(File::file:name_all(), Script::file:name_all()) -> {ok, Terms::[term()]} | {error, Reason::term()}. @@ -234,18 +293,26 @@ consult_and_eval(File, Script) -> Error end. +%% @private drops the .script extension from a filename. +-spec remove_script_ext(file:filename()) -> file:filename(). remove_script_ext(F) -> filename:rootname(F, ".script"). +%% @private sets up bindings for evaluations from a KV list. +-spec bs([{_,_}]) -> erl_eval:binding_struct(). bs(Vars) -> lists:foldl(fun({K,V}, Bs) -> erl_eval:add_binding(K, V, Bs) end, erl_eval:new_bindings(), Vars). -%% Find deps that have been added to the config after the lock was created +%% @private Find deps that have been added to the config after the lock was created +-spec find_newly_added(list(), list()) -> list(). find_newly_added(ConfigDeps, LockedDeps) -> [D || {true, D} <- [check_newly_added(Dep, LockedDeps) || Dep <- ConfigDeps]]. +%% @private checks if a given dependency is not within the lock file. +%% TODO: refine types for dependencies +-spec check_newly_added(term(), list()) -> false | {true, term()}. check_newly_added({_, _}=Dep, LockedDeps) -> check_newly_added_(Dep, LockedDeps); check_newly_added({_, _, {pkg, _}}=Dep, LockedDeps) -> @@ -255,6 +322,10 @@ check_newly_added({Name, _, Source}, LockedDeps) -> check_newly_added(Dep, LockedDeps) -> check_newly_added_(Dep, LockedDeps). +%% @private checks if a given dependency is not within the lock file. +%% TODO: refine types for dependencies +%% @end +-spec check_newly_added_(term(), list()) -> false | {true, term()}. %% get [raw] deps out of the way check_newly_added_({Name, Source, Opts}, LockedDeps) when is_tuple(Source), is_list(Opts) -> @@ -306,6 +377,9 @@ check_newly_added_(Dep, LockedDeps) when is_atom(Dep) -> check_newly_added_(Dep, _) -> throw(?PRV_ERROR({bad_dep_name, Dep})). +%% @private returns the name/path of the default config file, or its +%% override from the OS ENV var `REBAR_CONFIG'. +-spec config_file() -> file:filename(). config_file() -> case os:getenv("REBAR_CONFIG") of false -> diff --git a/src/rebar_core.erl b/src/rebar_core.erl index da8c3e6..14c4906 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -24,6 +24,8 @@ %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- +%% @doc Module providing core functionality about command dispatch, namespacing, +%% and chaining for rebar3. -module(rebar_core). -export([init_command/2, process_namespace/2, process_command/2, do/2, format_error/1]). @@ -31,6 +33,12 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). +%% @doc initial command set up; based on the first fragment of the +%% command, dispatch to special environments. The keywords for +%% `do' and `as' are implicitly reserved here, barring them from +%% being used as other commands or namespaces. +-spec init_command(rebar_state:t(), atom()) -> + {ok, rebar_state:t()} | {error, term()}. init_command(State, do) -> process_command(rebar_state:namespace(State, default), do); init_command(State, as) -> @@ -43,6 +51,14 @@ init_command(State, Command) -> {error, Reason} end. +%% @doc parse the commands starting at the namespace level; +%% a namespace is found if the first keyword to match is not +%% belonging to an existing provider, and iff the keyword also +%% matches a registered namespace. +%% The command to run is returned last; for namespaces, some +%% magic is done implicitly calling `do' as an indirect dispatcher. +-spec process_namespace(rebar_state:t(), atom()) -> + {error, term()} | {ok, rebar_state:t(), atom()}. process_namespace(_State, as) -> {error, "Namespace 'as' is forbidden"}; process_namespace(State, Command) -> @@ -61,7 +77,15 @@ process_namespace(State, Command) -> {ok, rebar_state:namespace(State, default), Command} end. --spec process_command(rebar_state:t(), atom()) -> {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}. +%% @doc Dispatches a given command based on the current state. +%% This requires mapping a command name to a specific provider. +%% `as' and `do' are still treated as special providers here. +%% Basic profile application may also be run. +%% +%% The function also takes care of expanding a provider to its +%% dependencies in the proper order. +-spec process_command(rebar_state:t(), atom()) -> + {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}. process_command(State, Command) -> %% ? rebar_prv_install_deps:setup_env(State), Providers = rebar_state:providers(State), @@ -104,7 +128,11 @@ process_command(State, Command) -> end end. --spec do([{atom(), atom()}], rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}. +%% @doc execute the selected providers. If a chain of providers +%% has been returned, run them one after the other, while piping +%% the state from the first into the next one. +-spec do([{atom(), atom()}], rebar_state:t()) -> + {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}. do([], State) -> {ok, State}; do([ProviderName | Rest], State) -> @@ -142,6 +170,8 @@ do([ProviderName | Rest], State) -> {error, ProviderName} end. +%% @doc convert a given exception's payload into an io description. +-spec format_error(any()) -> iolist(). format_error({bad_provider_namespace, {Namespace, Name}}) -> io_lib:format("Undefined command ~s in namespace ~s", [Name, Namespace]); format_error({bad_provider_namespace, Name}) -> diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index 79a1c7f..32cb264 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -115,10 +115,16 @@ template_globals(State) -> template_dir(State) -> filename:join([global_config_dir(State), "templates"]). +%% @doc checks if the current working directory is the base directory +%% for the project. +-spec processing_base_dir(rebar_state:t()) -> boolean(). processing_base_dir(State) -> Cwd = get_cwd(), processing_base_dir(State, Cwd). +%% @doc checks if the passed in directory is the base directory for +%% the project. +-spec processing_base_dir(rebar_state:t(), file:filename()) -> boolean(). processing_base_dir(State, Dir) -> AbsDir = filename:absname(Dir), AbsDir =:= rebar_state:get(State, base_dir). @@ -150,11 +156,25 @@ make_normalized_path([H|T], NormalizedPath) -> _ -> make_normalized_path(T, [H|NormalizedPath]) end. +%% @doc take a source and a target path, and relativize the target path +%% onto the source. +%% +%% Example: +%% ``` +%% 1> rebar_dir:make_relative_path("a/b/c/d/file", "a/b/file"). +%% "c/d/file" +%% 2> rebar_dir:make_relative_path("a/b/file", "a/b/c/d/file"). +%% "../../file" +%% ''' +-spec make_relative_path(file:filename(), file:filename()) -> file:filename(). make_relative_path(Source, Target) -> AbsSource = make_normalized_path(Source), AbsTarget = make_normalized_path(Target), do_make_relative_path(filename:split(AbsSource), filename:split(AbsTarget)). +%% @private based on fragments of paths, replace the number of common +%% segments by `../' bits, and add the rest of the source alone after it +-spec do_make_relative_path([string()], [string()]) -> file:filename(). do_make_relative_path([H|T1], [H|T2]) -> do_make_relative_path(T1, T2); do_make_relative_path(Source, Target) -> diff --git a/src/rebar_dist_utils.erl b/src/rebar_dist_utils.erl index f462826..03f4392 100644 --- a/src/rebar_dist_utils.erl +++ b/src/rebar_dist_utils.erl @@ -25,7 +25,7 @@ short(Name, Opts) -> long(Name, Opts) -> start(Name, longnames, Opts). --spec find_options(rebar_state:state()) -> {Long, Short, Opts} when +-spec find_options(rebar_state:t()) -> {Long, Short, Opts} when Long :: atom(), Short :: atom(), Opts :: [{setcookie,term()}]. diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index f2467c5..8645641 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -300,9 +300,8 @@ canonical_path([_|Acc], [".."|Rest]) -> canonical_path(Acc, Rest); canonical_path([], [".."|Rest]) -> canonical_path([], Rest); canonical_path(Acc, [Component|Rest]) -> canonical_path([Component|Acc], Rest). -%% returns canonical target of path if path is a link, otherwise returns path +%% @doc returns canonical target of path if path is a link, otherwise returns path -spec resolve_link(string()) -> string(). - resolve_link(Path) -> case file:read_link(Path) of {ok, Target} -> @@ -310,9 +309,8 @@ resolve_link(Path) -> {error, _} -> Path end. -%% splits a path into dirname and basename +%% @doc splits a path into dirname and basename -spec split_dirname(string()) -> {string(), string()}. - split_dirname(Path) -> {filename:dirname(Path), filename:basename(Path)}. diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 5817817..8a84ce3 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -135,6 +135,9 @@ etag(Path) -> false end. +%% @doc returns the SSL options adequate for the project based on +%% its configuration, including for validation of certs. +-spec ssl_opts(string()) -> [term()]. ssl_opts(Url) -> case get_ssl_config() of ssl_verify_enabled -> @@ -143,6 +146,9 @@ ssl_opts(Url) -> [{verify, verify_none}] end. +%% @doc returns the SSL options adequate for the project based on +%% its configuration, including for validation of certs. +-spec ssl_opts(atom(), string()) -> [term()]. ssl_opts(ssl_verify_enabled, Url) -> case check_ssl_version() of true -> diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index a8a7ea0..9c5e8ac 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -101,6 +101,7 @@ do_(State) -> {error, Reason} end. +%% @doc convert a given exception's payload into an io description. -spec format_error(any()) -> iolist(). format_error({dep_app_not_found, AppDir, AppName}) -> io_lib:format("Dependency failure: Application ~s not found at the top level of directory ~s", [AppName, AppDir]); @@ -125,8 +126,14 @@ format_error({cycles, Cycles}) -> format_error(Reason) -> io_lib:format("~p", [Reason]). -%% Allows other providers to install deps in a given profile +%% @doc Allows other providers to install deps in a given profile %% manually, outside of what is provided by rebar3's deps tuple. +-spec handle_deps_as_profile(Profile, State, Deps, Upgrade) -> {Apps, State} when + Profile :: atom(), + State :: rebar_state:t(), + Deps :: [tuple() | atom() | binary()], % TODO: meta to source() | lock() + Upgrade :: boolean(), + Apps :: [rebar_app_info:t()]. handle_deps_as_profile(Profile, State, Deps, Upgrade) -> Locks = [], Level = 0, diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index f55f40f..d92f119 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -95,6 +95,12 @@ sort_deps(Deps) -> droplast(L) -> lists:reverse(tl(lists:reverse(L))). +%% @doc filtermap takes in a function that is either or both +%% a predicate and a map, and returns the matching and valid elements. +-spec filtermap(F, [In]) -> [Out] when + F :: fun((In) -> boolean() | {true, Out}), + In :: term(), + Out :: term(). filtermap(F, [Hd|Tail]) -> case F(Hd) of true -> @@ -114,11 +120,16 @@ is_arch(ArchRegex) -> false end. +%% @doc returns the sytem architecture, in strings like +%% `"19.0.4-x86_64-unknown-linux-gnu-64"'. +-spec get_arch() -> string(). get_arch() -> Words = wordsize(), otp_release() ++ "-" ++ erlang:system_info(system_architecture) ++ "-" ++ Words. +%% @doc returns the size of a word on the system, as a string +-spec wordsize() -> string(). wordsize() -> try erlang:system_info({wordsize, external}) of Val -> @@ -502,11 +513,10 @@ patch_on_windows(Cmd, Env) -> Cmd end. -%% -%% Given env. variable FOO we want to expand all references to -%% it in InStr. References can have two forms: $FOO and ${FOO} -%% The end of form $FOO is delimited with whitespace or eol -%% +%% @doc Given env. variable `FOO' we want to expand all references to +%% it in `InStr'. References can have two forms: `$FOO' and `${FOO}' +%% The end of form `$FOO' is delimited with whitespace or EOL +-spec expand_env_variable(string(), string(), term()) -> string(). expand_env_variable(InStr, VarName, RawVarValue) -> case string:chr(InStr, $$) of 0 -> @@ -748,6 +758,9 @@ remove_from_code_path(Paths) -> code:del_path(Path) end, Paths). +%% @doc Revert to only having the beams necessary for running rebar3 and +%% plugins in the path +-spec cleanup_code_path([string()]) -> true | {error, term()}. cleanup_code_path(OrigPath) -> CurrentPath = code:get_path(), AddedPaths = CurrentPath -- OrigPath, -- cgit v1.1 From f605b917b4752f77bb349101573b0f72dec28ad8 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Mon, 28 Nov 2016 11:37:55 -0500 Subject: Replace unprocessed ~n linebreaks Fixes #1392 --- src/rebar_prv_install_deps.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index a8a7ea0..84a0563 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -119,9 +119,9 @@ format_error({missing_package, Package}) -> format_error({cycles, Cycles}) -> Prints = [["applications: ", [io_lib:format("~s ", [Dep]) || Dep <- Cycle], - "depend on each other~n"] + "depend on each other\n"] || Cycle <- Cycles], - ["Dependency cycle(s) detected:~n", Prints]; + ["Dependency cycle(s) detected:\n", Prints]; format_error(Reason) -> io_lib:format("~p", [Reason]). -- cgit v1.1 From 72dcbe1c1b5204e2e92b80a25cac6acf54254ec2 Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Tue, 29 Nov 2016 12:30:29 -0500 Subject: 1394: fixed handling of proxy username and password --- bootstrap | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/bootstrap b/bootstrap index c36fddb..9190c6b 100755 --- a/bootstrap +++ b/bootstrap @@ -99,8 +99,9 @@ fetch({pkg, Name, Vsn}, App) -> {ok, Binary} -> {ok, Contents} = extract(Binary), ok = erl_tar:extract({binary, Contents}, [{cwd, Dir}, compressed]); - _ -> - io:format("Error: Unable to fetch package ~p ~p~n", [Name, Vsn]) + {error, {Reason, _}} -> + ReasonText = re:replace(atom_to_list(Reason), "_", " ", [global,{return,list}]), + io:format("Error: Unable to fetch package ~s ~s: ~s~n", [Name, Vsn, ReasonText]) end; true -> io:format("Dependency ~s already exists~n", [Name]) @@ -112,8 +113,10 @@ extract(Binary) -> {ok, Contents}. request(Url) -> + HttpOptions = [{relaxed, true} | get_proxy_auth()], + case httpc:request(get, {Url, []}, - [{relaxed, true}], + HttpOptions, [{body_format, binary}], rebar) of {ok, {{_Version, 200, _Reason}, _Headers, Body}} -> @@ -402,3 +405,23 @@ otp_release1(Rel) -> binary:bin_to_list(Vsn, {0, Size - 1}) end end. + +%% extracts username and password from HTTPS_PROXY and returns them as tuple +get_proxy_auth() -> + get_proxy_auth(get_http_vars(https_proxy)). + +get_proxy_auth([]) -> + []; +get_proxy_auth(HttpsProxy) -> + {ok, {_, UserInfo, _, _, _, _}} = http_uri:parse(HttpsProxy), + parse_user_info(UserInfo). + +parse_user_info([]) -> + []; +parse_user_info(UserInfo) -> + Idx = string:chr(UserInfo, $:), + Username = string:sub_string(UserInfo, 1, Idx-1), + Password = string:sub_string(UserInfo, Idx+1), + %% password may contain url encoded characters, need to decode them first + [{proxy_auth, {Username, http_uri:decode(Password)}}]. + -- cgit v1.1 From ffc2cf98d390a35cf3f61c7c0bf5f26e3552fd2c Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Wed, 30 Nov 2016 13:45:06 -0500 Subject: 1394: added fix for rebar_utils, moved setting of http_options into init_config, added unit tests --- bootstrap | 29 +++++++++++++---------------- src/rebar3.erl | 3 ++- src/rebar_pkg_resource.erl | 4 +++- src/rebar_utils.erl | 24 +++++++++++++++++++++--- test/rebar_utils_SUITE.erl | 18 ++++++++++++++++-- 5 files changed, 55 insertions(+), 23 deletions(-) diff --git a/bootstrap b/bootstrap index 9190c6b..c7f0e06 100755 --- a/bootstrap +++ b/bootstrap @@ -150,8 +150,9 @@ set_httpc_options(_, []) -> ok; set_httpc_options(Scheme, Proxy) -> - {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), - httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). + {ok, {_, UserInfo, Host, Port, _, _}} = http_uri:parse(Proxy), + httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar), + set_proxy_auth(UserInfo). compile(App, FirstFiles) -> Dir = filename:join(filename:absname("_build/default/lib/"), App), @@ -406,22 +407,18 @@ otp_release1(Rel) -> end end. -%% extracts username and password from HTTPS_PROXY and returns them as tuple -get_proxy_auth() -> - get_proxy_auth(get_http_vars(https_proxy)). - -get_proxy_auth([]) -> - []; -get_proxy_auth(HttpsProxy) -> - {ok, {_, UserInfo, _, _, _, _}} = http_uri:parse(HttpsProxy), - parse_user_info(UserInfo). - -parse_user_info([]) -> - []; -parse_user_info(UserInfo) -> +set_proxy_auth([]) -> + ok; +set_proxy_auth(UserInfo) -> Idx = string:chr(UserInfo, $:), Username = string:sub_string(UserInfo, 1, Idx-1), Password = string:sub_string(UserInfo, Idx+1), %% password may contain url encoded characters, need to decode them first - [{proxy_auth, {Username, http_uri:decode(Password)}}]. + put(proxy_auth, [{proxy_auth, {Username, http_uri:decode(Password)}}]). + +get_proxy_auth() -> + case get(proxy_auth) of + undefined -> []; + ProxyAuth -> ProxyAuth + end. diff --git a/src/rebar3.erl b/src/rebar3.erl index 4e7e284..1059904 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -145,6 +145,8 @@ run_aux(State, RawArgs) -> rebar_core:init_command(rebar_state:command_args(State10, Args), Task). init_config() -> + rebar_utils:set_httpc_options(), + %% Initialize logging system Verbosity = log_level(), ok = rebar_log:init(command_line, Verbosity), @@ -320,7 +322,6 @@ ensure_running(App, Caller) -> end. state_from_global_config(Config, GlobalConfigFile) -> - rebar_utils:set_httpc_options(), GlobalConfigTerms = rebar_config:consult_file(GlobalConfigFile), GlobalConfig = rebar_state:new(GlobalConfigTerms), diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 5817817..8e1713d 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -107,8 +107,10 @@ make_vsn(_) -> {error, "Replacing version of type pkg not supported."}. request(Url, ETag) -> + HttpOptions = [{ssl, ssl_opts(Url)}, {relaxed, true} | rebar_utils:get_proxy_auth()], + case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]++[{"User-Agent", rebar_utils:user_agent()}]}, - [{ssl, ssl_opts(Url)}, {relaxed, true}], + HttpOptions, [{body_format, binary}], rebar) of {ok, {{_Version, 200, _Reason}, Headers, Body}} -> diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index f55f40f..4b43911 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -70,7 +70,9 @@ info_useless/2, list_dir/1, user_agent/0, - reread_config/1]). + reread_config/1, + get_proxy_auth/0, + set_proxy_auth/1]). %% for internal use only -export([otp_release/0]). @@ -825,8 +827,9 @@ set_httpc_options(_, []) -> ok; set_httpc_options(Scheme, Proxy) -> - {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), - httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). + {ok, {_, UserInfo, Host, Port, _, _}} = http_uri:parse(Proxy), + httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar), + set_proxy_auth(UserInfo). url_append_path(Url, ExtraPath) -> case http_uri:parse(Url) of @@ -865,3 +868,18 @@ list_dir(Dir) -> true -> file:list_dir_all(Dir); false -> file:list_dir(Dir) end. + +set_proxy_auth([]) -> + ok; +set_proxy_auth(UserInfo) -> + Idx = string:chr(UserInfo, $:), + Username = string:sub_string(UserInfo, 1, Idx-1), + Password = string:sub_string(UserInfo, Idx+1), + %% password may contain url encoded characters, need to decode them first + application:set_env(rebar, proxy_auth, [{proxy_auth, {Username, http_uri:decode(Password)}}]). + +get_proxy_auth() -> + case application:get_env(rebar, proxy_auth) of + undefined -> []; + {ok, ProxyAuth} -> ProxyAuth + end. diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index b32992d..0d496a3 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -31,7 +31,8 @@ nonblacklisted_otp_version/1, blacklisted_otp_version/1, sh_does_not_miss_messages/1, - tup_merge/1]). + tup_merge/1, + proxy_auth/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -46,7 +47,8 @@ end_per_testcase(_, _Config) -> all() -> [{group, args_to_tasks}, sh_does_not_miss_messages, - tup_merge]. + tup_merge, + proxy_auth]. groups() -> [{args_to_tasks, [], [empty_arglist, @@ -272,3 +274,15 @@ tup_merge(_Config) -> rebar_utils:tup_sort([{a,a},{a,a,a},a,{b,a,a},b,{z,a},{z,a,a},{b,a},z]) ) ). + +proxy_auth(_Config) -> + %% proxy auth with regular username/password + rebar_utils:set_proxy_auth("Username", "Password"), + ?assertEqual([{proxy_auth, {"Username", "Password"}}], + rebar_utils:get_proxy_auth()), + %% proxy auth with username missing and url encoded password + rebar_utils:set_proxy_auth("", "?!abc#$"), + ?assertEqual([{proxy_auth, {"", "%3F!abc%23%24"}}], + rebar_utils:get_proxy_auth()). + + -- cgit v1.1 From 8a546cae8eab79ea2107476e0978247457d89acb Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Wed, 30 Nov 2016 13:48:22 -0500 Subject: 1394: fixed typo --- test/rebar_utils_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index 0d496a3..6cbac1a 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -277,11 +277,11 @@ tup_merge(_Config) -> proxy_auth(_Config) -> %% proxy auth with regular username/password - rebar_utils:set_proxy_auth("Username", "Password"), + rebar_utils:set_proxy_auth("Username:Password"), ?assertEqual([{proxy_auth, {"Username", "Password"}}], rebar_utils:get_proxy_auth()), %% proxy auth with username missing and url encoded password - rebar_utils:set_proxy_auth("", "?!abc#$"), + rebar_utils:set_proxy_auth(":?!abc#$"), ?assertEqual([{proxy_auth, {"", "%3F!abc%23%24"}}], rebar_utils:get_proxy_auth()). -- cgit v1.1 From f8873147abe49baa4e692b6dd93be1b8db89e172 Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Wed, 30 Nov 2016 13:52:23 -0500 Subject: 1394: fixed typos --- test/rebar_utils_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index 6cbac1a..ad37e00 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -281,8 +281,8 @@ proxy_auth(_Config) -> ?assertEqual([{proxy_auth, {"Username", "Password"}}], rebar_utils:get_proxy_auth()), %% proxy auth with username missing and url encoded password - rebar_utils:set_proxy_auth(":?!abc#$"), - ?assertEqual([{proxy_auth, {"", "%3F!abc%23%24"}}], + rebar_utils:set_proxy_auth(":%3F!abc%23%24"), + ?assertEqual([{proxy_auth, {"", "?!abc#$"}}], rebar_utils:get_proxy_auth()). -- cgit v1.1 From e60562fb307a8ff59f56746427e60ed3b2a1227a Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Thu, 1 Dec 2016 02:49:53 -0500 Subject: 1394: one more test --- test/rebar_utils_SUITE.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index ad37e00..e8f33a3 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -276,6 +276,9 @@ tup_merge(_Config) -> ). proxy_auth(_Config) -> + application:unset_env(rebar, proxy_auth), + %% proxy auth not set + ?assertEqual([], rebar_utils:get_proxy_auth()), %% proxy auth with regular username/password rebar_utils:set_proxy_auth("Username:Password"), ?assertEqual([{proxy_auth, {"Username", "Password"}}], -- cgit v1.1 From c0184eae706f6c2bf7bcf8fc1db03c0569cb73c1 Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Thu, 1 Dec 2016 10:27:07 -0500 Subject: 1394: refined export list and tests --- src/rebar_utils.erl | 4 ++-- test/rebar_utils_SUITE.erl | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 4b43911..a0a44d1 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -71,8 +71,8 @@ list_dir/1, user_agent/0, reread_config/1, - get_proxy_auth/0, - set_proxy_auth/1]). + get_proxy_auth/0]). + %% for internal use only -export([otp_release/0]). diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index e8f33a3..6307b42 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -276,15 +276,20 @@ tup_merge(_Config) -> ). proxy_auth(_Config) -> + Host = "host:", + Port = "1234", + application:unset_env(rebar, proxy_auth), %% proxy auth not set ?assertEqual([], rebar_utils:get_proxy_auth()), %% proxy auth with regular username/password - rebar_utils:set_proxy_auth("Username:Password"), + os:putenv("http_proxy", "http://Username:Password@" ++ Host ++ Port), + rebar_utils:set_httpc_options(), ?assertEqual([{proxy_auth, {"Username", "Password"}}], rebar_utils:get_proxy_auth()), %% proxy auth with username missing and url encoded password - rebar_utils:set_proxy_auth(":%3F!abc%23%24"), + os:putenv("http_proxy", "http://:%3F!abc%23%24@" ++ Host ++ Port), + rebar_utils:set_httpc_options(), ?assertEqual([{proxy_auth, {"", "?!abc#$"}}], rebar_utils:get_proxy_auth()). -- cgit v1.1 From 603683bca1ec067e7490405fd308cdabbc442702 Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Thu, 1 Dec 2016 11:16:33 -0500 Subject: 1394: restore original proxy spec after tests --- test/rebar_utils_SUITE.erl | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index 6307b42..bdbffb0 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -278,19 +278,34 @@ tup_merge(_Config) -> proxy_auth(_Config) -> Host = "host:", Port = "1234", + + proxy_auth(_Config, "http_proxy"), + proxy_auth(_Config, "https_proxy"). + +proxy_auth(_Config, ProxyEnvKey) -> + %% remember current proxy specification + OldProxySpec = os:getenv(ProxyEnvKey), - application:unset_env(rebar, proxy_auth), %% proxy auth not set + application:unset_env(rebar, proxy_auth), ?assertEqual([], rebar_utils:get_proxy_auth()), + %% proxy auth with regular username/password - os:putenv("http_proxy", "http://Username:Password@" ++ Host ++ Port), + os:putenv(ProxyEnvKey, "http://Username:Password@" ++ Host ++ Port), rebar_utils:set_httpc_options(), ?assertEqual([{proxy_auth, {"Username", "Password"}}], rebar_utils:get_proxy_auth()), + %% proxy auth with username missing and url encoded password - os:putenv("http_proxy", "http://:%3F!abc%23%24@" ++ Host ++ Port), + os:putenv(ProxyEnvKey, "http://:%3F!abc%23%24@" ++ Host ++ Port), rebar_utils:set_httpc_options(), ?assertEqual([{proxy_auth, {"", "?!abc#$"}}], - rebar_utils:get_proxy_auth()). - - + rebar_utils:get_proxy_auth()), + + %% restore original proxy specification if any + restore_proxy_env(OldProxySpec). + +restore_proxy_env(false) -> + ok; +restore_proxy_env(ProxySpec) -> + os:putenv("http_proxy", ProxySpec). -- cgit v1.1 From 9ace3ba9fc1b2a1a7096e4fe62d9867f737cff1d Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Thu, 1 Dec 2016 11:17:07 -0500 Subject: 1394: fixed typo --- test/rebar_utils_SUITE.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index bdbffb0..57b6474 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -276,13 +276,13 @@ tup_merge(_Config) -> ). proxy_auth(_Config) -> - Host = "host:", - Port = "1234", - proxy_auth(_Config, "http_proxy"), proxy_auth(_Config, "https_proxy"). proxy_auth(_Config, ProxyEnvKey) -> + Host = "host:", + Port = "1234", + %% remember current proxy specification OldProxySpec = os:getenv(ProxyEnvKey), -- cgit v1.1 From 8df95d53bfd5dc892e69364dfe87ecf95c6897a2 Mon Sep 17 00:00:00 2001 From: Artem Pervin Date: Thu, 1 Dec 2016 11:20:58 -0500 Subject: 1394: fixed typo --- test/rebar_utils_SUITE.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index 57b6474..dd92bbf 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -303,9 +303,9 @@ proxy_auth(_Config, ProxyEnvKey) -> rebar_utils:get_proxy_auth()), %% restore original proxy specification if any - restore_proxy_env(OldProxySpec). + restore_proxy_env(ProxyEnvKey, OldProxySpec). -restore_proxy_env(false) -> +restore_proxy_env(_, false) -> ok; -restore_proxy_env(ProxySpec) -> - os:putenv("http_proxy", ProxySpec). +restore_proxy_env(ProxyEnvKey, ProxySpec) -> + os:putenv(ProxyEnvKey, ProxySpec). -- cgit v1.1 From 2c155ead23abeedb39ff761e4db5269f7b2a78ca Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 1 Dec 2016 11:47:05 -0500 Subject: Fully clean up after test utils for proxy --- test/rebar_utils_SUITE.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index dd92bbf..8b8769b 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -303,9 +303,10 @@ proxy_auth(_Config, ProxyEnvKey) -> rebar_utils:get_proxy_auth()), %% restore original proxy specification if any - restore_proxy_env(ProxyEnvKey, OldProxySpec). + restore_proxy_env(ProxyEnvKey, OldProxySpec), + application:unset_env(rebar, proxy_auth). -restore_proxy_env(_, false) -> - ok; +restore_proxy_env(ProxyEnvKey, false) -> + os:putenv(ProxyEnvKey, ""); restore_proxy_env(ProxyEnvKey, ProxySpec) -> - os:putenv(ProxyEnvKey, ProxySpec). + os:putenv(ProxyEnvKey, ProxySpec). -- cgit v1.1 From 03832379d6e78c3788c305f43728b485410ac5e4 Mon Sep 17 00:00:00 2001 From: Ted Burghart Date: Wed, 30 Nov 2016 17:22:42 -0500 Subject: Addresses https://github.com/erlang/rebar3/issues/1397 Ensures merged compiler options end up in the correct order to maintain profile precedence. Moves the merge functionality from rebar_opts:merge_opts/2 to a standalone function to ease extension and debugging. --- src/rebar_opts.erl | 93 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/src/rebar_opts.erl b/src/rebar_opts.erl index b02a504..444b760 100644 --- a/src/rebar_opts.erl +++ b/src/rebar_opts.erl @@ -101,43 +101,70 @@ merge_opts(Profile, NewOpts, OldOpts) -> end. merge_opts(NewOpts, OldOpts) -> - dict:merge(fun(deps, _NewValue, OldValue) -> - OldValue; - ({deps, _}, NewValue, _OldValue) -> - NewValue; - (plugins, NewValue, _OldValue) -> - NewValue; - ({plugins, _}, NewValue, _OldValue) -> - NewValue; - (profiles, NewValue, OldValue) -> - dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); - (mib_first_files, Value, Value) -> - Value; - (mib_first_files, NewValue, OldValue) -> - OldValue ++ NewValue; - (relx, NewValue, OldValue) -> - rebar_utils:tup_umerge(OldValue, NewValue); - (_Key, NewValue, OldValue) when is_list(NewValue) -> - case io_lib:printable_list(NewValue) of - true when NewValue =:= [] -> - case io_lib:printable_list(OldValue) of - true -> - NewValue; - false -> - OldValue - end; - true -> - NewValue; - false -> - rebar_utils:tup_umerge(NewValue, OldValue) - end; - (_Key, NewValue, _OldValue) -> - NewValue - end, NewOpts, OldOpts). + dict:merge(fun merge_opt/3, NewOpts, OldOpts). %% Internal functions %% +%% Function for dict:merge/3 (in merge_opts/2) to merge options by priority. +%% +merge_opt(deps, _NewValue, OldValue) -> + OldValue; +merge_opt({deps, _}, NewValue, _OldValue) -> + NewValue; +merge_opt(plugins, NewValue, _OldValue) -> + NewValue; +merge_opt({plugins, _}, NewValue, _OldValue) -> + NewValue; +merge_opt(profiles, NewValue, OldValue) -> + dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); +merge_opt(mib_first_files, Value, Value) -> + Value; +merge_opt(mib_first_files, NewValue, OldValue) -> + OldValue ++ NewValue; +merge_opt(relx, NewValue, OldValue) -> + rebar_utils:tup_umerge(OldValue, NewValue); +merge_opt(Key, NewValue, OldValue) + when Key == erl_opts; Key == eunit_compile_opts; Key == ct_compile_opts -> + merge_erl_opts(lists:reverse(OldValue), NewValue); +merge_opt(_Key, NewValue, OldValue) when is_list(NewValue) -> + case io_lib:printable_list(NewValue) of + true when NewValue =:= [] -> + case io_lib:printable_list(OldValue) of + true -> + NewValue; + false -> + OldValue + end; + true -> + NewValue; + false -> + rebar_utils:tup_umerge(NewValue, OldValue) + end; +merge_opt(_Key, NewValue, _OldValue) -> + NewValue. + +%% +%% Merge Erlang compiler options such that the result +%% a) Doesn't contain duplicates. +%% b) Resulting options are ordered by increasing precedence as expected by +%% the compiler. +%% The first parameter is the lower precedence options, in reverse order, to +%% be merged with the higher-precedence options in the second parameter. +%% +merge_erl_opts([Opt | Opts], []) -> + merge_erl_opts(Opts, [Opt]); +merge_erl_opts([Opt | Opts], Merged) -> + case lists:member(Opt, Merged) of + true -> + merge_erl_opts(Opts, Merged); + _ -> + merge_erl_opts(Opts, [Opt | Merged]) + end; +merge_erl_opts([], Merged) -> + Merged. + +%% %% Filter a list of erl_opts platform_define options such that only %% those which match the provided architecture regex are returned. %% -- cgit v1.1 From 1b422b921b8921128213b29423a48cd7b2c8e417 Mon Sep 17 00:00:00 2001 From: Ted Burghart Date: Mon, 5 Dec 2016 13:22:35 -0500 Subject: Added regression tests for PR 1398 --- test/rebar_profiles_SUITE.erl | 104 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/test/rebar_profiles_SUITE.erl b/test/rebar_profiles_SUITE.erl index a31a4c9..ed492a9 100644 --- a/test/rebar_profiles_SUITE.erl +++ b/test/rebar_profiles_SUITE.erl @@ -20,7 +20,12 @@ test_profile_applied_at_completion/1, test_profile_applied_before_compile/1, test_profile_applied_before_eunit/1, - test_profile_applied_to_apps/1]). + test_profile_applied_to_apps/1, + test_profile_erl_opts_order_1/1, + test_profile_erl_opts_order_2/1, + test_profile_erl_opts_order_3/1, + test_profile_erl_opts_order_4/1, + test_profile_erl_opts_order_5/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -36,7 +41,12 @@ all() -> test_profile_applied_at_completion, test_profile_applied_before_compile, test_profile_applied_before_eunit, - test_profile_applied_to_apps]. + test_profile_applied_to_apps, + test_profile_erl_opts_order_1, + test_profile_erl_opts_order_2, + test_profile_erl_opts_order_3, + test_profile_erl_opts_order_4, + test_profile_erl_opts_order_5]. init_per_suite(Config) -> application:start(meck), @@ -432,3 +442,93 @@ test_profile_applied_to_apps(Config) -> ErlOpts = dict:fetch(erl_opts, Opts), true = lists:member({d, 'TEST'}, ErlOpts) end, Apps). + +test_profile_erl_opts_order_1(Config) -> + Opts = get_compiled_profile_erl_opts([default], Config), + Opt = last_erl_opt(Opts, [warn_export_all, nowarn_export_all], undefined), + undefined = Opt. + +test_profile_erl_opts_order_2(Config) -> + Opts = get_compiled_profile_erl_opts([strict], Config), + Opt = last_erl_opt(Opts, [warn_export_all, nowarn_export_all], undefined), + warn_export_all = Opt. + +test_profile_erl_opts_order_3(Config) -> + Opts = get_compiled_profile_erl_opts([loose], Config), + Opt = last_erl_opt(Opts, [warn_export_all, nowarn_export_all], undefined), + nowarn_export_all = Opt. + +test_profile_erl_opts_order_4(Config) -> + Opts = get_compiled_profile_erl_opts([strict, loose], Config), + Opt = last_erl_opt(Opts, [warn_export_all, nowarn_export_all], undefined), + nowarn_export_all = Opt. + +test_profile_erl_opts_order_5(Config) -> + Opts = get_compiled_profile_erl_opts([loose, strict], Config), + Opt = last_erl_opt(Opts, [warn_export_all, nowarn_export_all], undefined), + warn_export_all = Opt. + +get_compiled_profile_erl_opts(Profiles, Config) -> + AppDir = ?config(apps, Config), + PStrs = [atom_to_list(P) || P <- Profiles], + + Name = rebar_test_utils:create_random_name( + lists:flatten(["erl_opts_order_" | [[S, $_] || S <- PStrs]])), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [ + {erl_opts, [warnings_as_errors, {d, profile_default}]}, + {profiles, [ + {strict, [{erl_opts, [warn_export_all, {d, profile_strict}]}]}, + {loose, [{erl_opts, [nowarn_export_all, {d, profile_loose}]}]} ]}], + rebar_test_utils:create_config(AppDir, RebarConfig), + + Command = case Profiles of + [] -> + ["compile"]; + [default] -> + ["compile"]; + _ -> + ["as", string:join(PStrs, ","), "compile"] + end, + {ok, State} = rebar_test_utils:run_and_check( + Config, RebarConfig, Command, {ok, [{app, Name}]}), + code:add_paths(rebar_state:code_paths(State, all_deps)), + Mod = list_to_atom(Name), + proplists:get_value(options, Mod:module_info(compile), []). + +% macro definitions get special handling +last_erl_opt([{d, Macro} = Opt | Opts], Targets, Last) -> + case lists:any(erl_opt_macro_match_fun(Macro), Targets) of + true -> + last_erl_opt(Opts, Targets, Opt); + _ -> + last_erl_opt(Opts, Targets, Last) + end; +last_erl_opt([{d, Macro, _} = Opt | Opts], Targets, Last) -> + case lists:any(erl_opt_macro_match_fun(Macro), Targets) of + true -> + last_erl_opt(Opts, Targets, Opt); + _ -> + last_erl_opt(Opts, Targets, Last) + end; +last_erl_opt([Opt | Opts], Targets, Last) -> + case lists:member(Opt, Targets) of + true -> + last_erl_opt(Opts, Targets, Opt); + _ -> + last_erl_opt(Opts, Targets, Last) + end; +last_erl_opt([], _, Last) -> + Last. + +erl_opt_macro_match_fun(Macro) -> + fun({d, M}) -> + M == Macro; + ({d, M, _}) -> + M == Macro; + (_) -> + false + end. + -- cgit v1.1 From 74d290b9c2be6352813c71ad6036a24ddacfae79 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 6 Dec 2016 11:31:28 +0100 Subject: Restrict regexp to match on files starting with '._' --- src/rebar_base_compiler.erl | 2 +- src/rebar_erlc_compiler.erl | 2 +- src/rebar_prv_eunit.erl | 2 +- src/rebar_templater.erl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 5d54057..bfd79a5 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -54,7 +54,7 @@ run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, Opts) -> %% Convert simple extension to proper regex - SourceExtRe = "^[^._].*\\" ++ SourceExt ++ [$$], + SourceExtRe = "^(?!._).*\\" ++ SourceExt ++ [$$], Recursive = proplists:get_value(recursive, Opts, true), %% Find all possible source files diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 325bb4f..d4d257f 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -48,7 +48,7 @@ -type compile_opt() :: {recursive, boolean()}. -define(DEFAULT_OUTDIR, "ebin"). --define(RE_PREFIX, "^[^._]"). +-define(RE_PREFIX, "^(?!._)"). %% =================================================================== %% Public API diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 0908ec9..6fdf33e 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -18,7 +18,7 @@ %% we need to modify app_info state before compile -define(DEPS, [lock]). --define(DEFAULT_TEST_REGEX, "^[^._].*\\.erl\$"). +-define(DEFAULT_TEST_REGEX, "^(?!._).*\\.erl\$"). %% =================================================================== %% Public API diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 299b957..7e0aae4 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -33,7 +33,7 @@ -include("rebar.hrl"). --define(TEMPLATE_RE, "^[^._].*\\.template\$"). +-define(TEMPLATE_RE, "^(?!._).*\\.template\$"). %% =================================================================== %% Public API -- cgit v1.1 From 85e00f2a434d3295d7a683838cda002403f5af17 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Mon, 28 Nov 2016 11:45:21 +0100 Subject: Improve merge of command line options and config options Bug: option 'spec' is not specifically handled when merging options from the command line with options from rebar.config. Due to this, if the config specifies a 'spec', then this will take precedence over any 'dir' and/or 'suite' on the command line. This commit takes special care of all options that can be used to select tests - meaning that if any of the options 'spec', 'dir', 'suite', 'group' or 'case' are specified on the command line, then all 'spec', 'dir', 'suite', 'group' and 'case' options in rebar.config will be ignored. --- src/rebar_prv_common_test.erl | 77 +++++++++++++++++++++++++------------- test/rebar_ct_SUITE.erl | 86 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 27 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index be31e8c..5fa7ae4 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -227,20 +227,42 @@ select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> rebar_utils:reread_config(Configs), code:set_path(OldPath), - Merged = lists:ukeymerge(1, - lists:ukeysort(1, CmdOpts), - lists:ukeysort(1, CfgOpts)), - %% make sure `dir` and/or `suite` from command line go in as - %% a pair overriding both `dir` and `suite` from config if - %% they exist - Opts = case {proplists:get_value(suite, CmdOpts), proplists:get_value(dir, CmdOpts)} of - {undefined, undefined} -> Merged; - {_Suite, undefined} -> lists:keydelete(dir, 1, Merged); - {undefined, _Dir} -> lists:keydelete(suite, 1, Merged); - {_Suite, _Dir} -> Merged - end, + Opts = merge_opts(CmdOpts,CfgOpts), discover_tests(State, ProjectApps, Opts). +%% Merge the option lists from command line and rebar.config: +%% +%% - Options set on the command line will replace the same options if +%% set in rebar.config. +%% +%% - Special care is taken with options that select which tests to +%% run - ANY such option on the command line will replace ALL such +%% options in the config. +%% +%% Note that if 'spec' is given, common_test will ignore all 'dir', +%% 'suite', 'group' and 'case', so there is no need to explicitly +%% remove any options from the command line. +%% +%% All faulty combinations of options are also handled by +%% common_test and are not taken into account here. +merge_opts(CmdOpts0, CfgOpts0) -> + TestSelectOpts = [spec,dir,suite,group,testcase], + CmdOpts = lists:ukeysort(1, CmdOpts0), + CfgOpts1 = lists:ukeysort(1, CfgOpts0), + CfgOpts = case is_any_defined(TestSelectOpts,CmdOpts) of + false -> + CfgOpts1; + true -> + [Opt || Opt={K,_} <- CfgOpts1, + not lists:member(K,TestSelectOpts)] + end, + lists:ukeymerge(1, CmdOpts, CfgOpts). + +is_any_defined([Key|Keys],Opts) -> + proplists:is_defined(Key,Opts) orelse is_any_defined(Keys,Opts); +is_any_defined([],_Opts) -> + false. + sys_config_list(CmdOpts, CfgOpts) -> CmdSysConfigs = split_string(proplists:get_value(sys_config, CmdOpts, "")), case proplists:get_value(sys_config, CfgOpts, []) of @@ -253,11 +275,10 @@ sys_config_list(CmdOpts, CfgOpts) -> end. discover_tests(State, ProjectApps, Opts) -> - case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of - %% no dirs or suites defined, try using `$APP/test` and `$ROOT/test` - %% as suites - {undefined, undefined} -> {ok, [default_tests(State, ProjectApps)|Opts]}; - {_, _} -> {ok, Opts} + case is_any_defined([spec,dir,suite],Opts) of + %% no tests defined, try using `$APP/test` and `$ROOT/test` as dirs + false -> {ok, [default_tests(State, ProjectApps)|Opts]}; + true -> {ok, Opts} end. default_tests(State, ProjectApps) -> @@ -397,14 +418,20 @@ readable(State) -> end. test_dirs(State, Apps, Opts) -> - case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of - {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); - {undefined, Dirs} -> set_compile_dirs(State, Apps, {dir, Dirs}); - {Suites, Dir} when is_integer(hd(Dir)) -> - set_compile_dirs(State, Apps, join(Suites, Dir)); - {Suites, [Dir]} when is_integer(hd(Dir)) -> - set_compile_dirs(State, Apps, join(Suites, Dir)); - {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} + case proplists:get_value(spec, Opts) of + undefined -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); + {undefined, Dirs} -> set_compile_dirs(State, Apps, {dir, Dirs}); + {Suites, Dir} when is_integer(hd(Dir)) -> + set_compile_dirs(State, Apps, join(Suites, Dir)); + {Suites, [Dir]} when is_integer(hd(Dir)) -> + set_compile_dirs(State, Apps, join(Suites, Dir)); + {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} + end; + _Specs -> + %% Currently not adding any directories from the spec files. + {ok, rebar_state:project_apps(State, Apps)} end. join(Suite, Dir) when is_integer(hd(Suite)) -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 8e989b5..1995690 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -51,7 +51,9 @@ cover_compiled/1, misspecified_ct_opts/1, misspecified_ct_compile_opts/1, - misspecified_ct_first_files/1]). + misspecified_ct_first_files/1, + testspec/1, + cmd_vs_cfg_opts/1]). -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -67,7 +69,9 @@ all() -> [{group, basic_app}, cfg_atom_suites, misspecified_ct_opts, misspecified_ct_compile_opts, - misspecified_ct_first_files]. + misspecified_ct_first_files, + testspec, + cmd_vs_cfg_opts]. groups() -> [{basic_app, [], [basic_app_default_dirs, basic_app_default_beams, @@ -1234,6 +1238,84 @@ misspecified_ct_first_files(Config) -> {badconfig, {"Value `~p' of option `~p' must be a list", {some_file, ct_first_files}}} = Error. +testspec(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_testspec_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_testspec_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + Spec = filename:join([AppDir, "test", "some.spec"]), + ok = filelib:ensure_dir(Spec), + ok = file:write_file(Spec, "[].\n"), + + {ok, State} = rebar_test_utils:run_and_check(C, + [], + ["as", "test", "lock"], + return), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + + {ok, GetOptResult1} = getopt:parse(GetOptSpec, ["--spec","test/some.spec"]), + State1 = rebar_state:command_parsed_args(State, GetOptResult1), + Tests1 = rebar_prv_common_test:prepare_tests(State1), + {ok, _} = rebar_prv_common_test:compile(State1, Tests1), + + ok. + +cmd_vs_cfg_opts(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_cmd_vs_cfg_opts_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_cmd_vs_cfg_opts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{ct_opts, [{spec,"mytest.spec"}, + {dir,"test"}, + {suite,"some_SUITE"}, + {group,"some_group"}, + {testcase,"some_test"}]}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State), + true = lists:member({spec, "mytest.spec"}, TestOpts), + true = lists:member({dir, "test"}, TestOpts), + true = lists:member({suite, "some_SUITE"}, TestOpts), + true = lists:member({group, "some_group"}, TestOpts), + true = lists:member({testcase, "some_test"}, TestOpts), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + + {ok, GetOptResult1} = getopt:parse(GetOptSpec, ["--spec","test/some.spec"]), + State1 = rebar_state:command_parsed_args(State, GetOptResult1), + {ok, TestOpts1} = rebar_prv_common_test:prepare_tests(State1), + true = lists:member({spec, ["test/some.spec"]}, TestOpts1), + false = lists:keymember(dir, 1, TestOpts1), + false = lists:keymember(suite, 1, TestOpts1), + false = lists:keymember(group, 1, TestOpts1), + false = lists:keymember(testcase, 1, TestOpts1), + + {ok, GetOptResult2} = getopt:parse(GetOptSpec, ["--suite","test/some_SUITE"]), + State2 = rebar_state:command_parsed_args(State, GetOptResult2), + {ok, TestOpts2} = rebar_prv_common_test:prepare_tests(State2), + true = lists:member({suite, ["test/some_SUITE"]}, TestOpts2), + false = lists:keymember(spec, 1, TestOpts2), + false = lists:keymember(dir, 1, TestOpts2), + false = lists:keymember(group, 1, TestOpts2), + false = lists:keymember(testcase, 1, TestOpts2), + + ok. + %% helper for generating test data test_suite(Name) -> io_lib:format("-module(~ts_SUITE).\n" -- cgit v1.1 From 3fd29af16cd55defaabfd08bf0bafe275a68a7c7 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Wed, 7 Dec 2016 11:03:00 -0800 Subject: add `get-deps` provider a no-op provider that depends on lock that is slightly more discoverable and user friendly --- src/rebar.app.src | 1 + src/rebar_prv_get_deps.erl | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/rebar_prv_get_deps.erl diff --git a/src/rebar.app.src b/src/rebar.app.src index 5b735cf..74efe97 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -52,6 +52,7 @@ rebar_prv_edoc, rebar_prv_escriptize, rebar_prv_eunit, + rebar_prv_get_deps, rebar_prv_help, rebar_prv_install_deps, rebar_prv_local_install, diff --git a/src/rebar_prv_get_deps.erl b/src/rebar_prv_get_deps.erl new file mode 100644 index 0000000..020e50b --- /dev/null +++ b/src/rebar_prv_get_deps.erl @@ -0,0 +1,37 @@ +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 et + +-module(rebar_prv_get_deps). + +-behaviour(provider). + +-export([init/1, + do/1, + format_error/1]). + +-define(PROVIDER, 'get-deps'). +-define(DEPS, [lock]). + +%% =================================================================== +%% Public API +%% =================================================================== + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + Provider = providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {deps, ?DEPS}, + {bare, true}, + {example, "rebar3 get-deps"}, + {short_desc, "Fetch dependencies."}, + {desc, "Fetch project dependencies."}, + {opts, []}, + {profiles, []}]), + {ok, rebar_state:add_provider(State, Provider)}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()}. +do(State) -> {ok, State}. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). \ No newline at end of file -- cgit v1.1 From 7780933cfb78fde053ec7c69ba8a20ac14535cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Fri, 2 Dec 2016 14:36:00 +0100 Subject: Use different git commands for different git versions The option --single-branch was introduced in git version 1.7.10 and thus rebar3 cannot fetch git dependencies on systems where earlier git versions are install. This commit will select other git clone commands if an earlier git version is detected. If the git version cannot be determined rebar3 falls back on the previous behavior and uses --single-branch. --- src/rebar_git_resource.erl | 72 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index acb9ec0..201b8b6 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -107,28 +107,50 @@ download(Dir, {git, Url, ""}, State) -> download(Dir, {git, Url, {branch, "master"}}, State); download(Dir, {git, Url, {branch, Branch}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", - [rebar_utils:escape_chars(Url), - rebar_utils:escape_chars(filename:basename(Dir)), - rebar_utils:escape_chars(Branch)]), - [{cd, filename:dirname(Dir)}]); + git_clone(branch, git_vsn(), Url, Dir, Branch); download(Dir, {git, Url, {tag, Tag}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", - [rebar_utils:escape_chars(Url), - rebar_utils:escape_chars(filename:basename(Dir)), - rebar_utils:escape_chars(Tag)]), - [{cd, filename:dirname(Dir)}]); + git_clone(tag, git_vsn(), Url, Dir, Tag); download(Dir, {git, Url, {ref, Ref}}, _State) -> ok = filelib:ensure_dir(Dir), + git_clone(ref, git_vsn(), Url, Dir, Ref); +download(Dir, {git, Url, Rev}, _State) -> + ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []), + ok = filelib:ensure_dir(Dir), + git_clone(rev, git_vsn(), Url, Dir, Rev). + +%% Use different git clone commands depending on git --version +git_clone(branch,Vsn,Url,Dir,Branch) when Vsn >= {1,7,10}; Vsn =:= undefined -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Branch)]), + [{cd, filename:dirname(Dir)}]); +git_clone(branch,_Vsn,Url,Dir,Branch) -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Branch)]), + [{cd, filename:dirname(Dir)}]); +git_clone(tag,Vsn,Url,Dir,Tag) when Vsn >= {1,7,10}; Vsn =:= undefined -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Tag)]), + [{cd, filename:dirname(Dir)}]); +git_clone(tag,_Vsn,Url,Dir,Tag) -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Tag)]), + [{cd, filename:dirname(Dir)}]); +git_clone(ref,_Vsn,Url,Dir,Ref) -> rebar_utils:sh(?FMT("git clone -n ~s ~s", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]), rebar_utils:sh(?FMT("git checkout -q ~s", [Ref]), [{cd, Dir}]); -download(Dir, {git, Url, Rev}, _State) -> - ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []), - ok = filelib:ensure_dir(Dir), +git_clone(rev,_Vsn,Url,Dir,Rev) -> rebar_utils:sh(?FMT("git clone -n ~s ~s", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), @@ -136,6 +158,30 @@ download(Dir, {git, Url, Rev}, _State) -> rebar_utils:sh(?FMT("git checkout -q ~s", [rebar_utils:escape_chars(Rev)]), [{cd, Dir}]). +git_vsn() -> + case application:get_env(rebar, git_vsn) of + {ok, Vsn} -> Vsn; + undefined -> + Vsn = git_vsn_fetch(), + application:set_env(rebar, git_vsn, Vsn), + Vsn + end. + +git_vsn_fetch() -> + case rebar_utils:sh("git --version",[]) of + {ok, VsnStr} -> + case re:run(VsnStr, "git version\\h+(\\d)\\.(\\d)\\.(\\d).*",[{capture,[1,2,3],list}]) of + {match,[Maj,Min,Patch]} -> + {list_to_integer(Maj), + list_to_integer(Min), + list_to_integer(Patch)}; + nomatch -> + undefined + end; + {error, _} -> + undefined + end. + make_vsn(Dir) -> case collect_default_refcount(Dir) of Vsn={plain, _} -> -- cgit v1.1 From a0e7ff2eb95a8093f666d310bd5df6395a243bd8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 8 Dec 2016 15:46:35 +0100 Subject: Add directory of testspec as extra_src_dir This is necessary in order to automatically get the testspec included as an artifact (i.e. copied to the _build dir) in the case when it is stored in another directory than 'test'. --- src/rebar_prv_common_test.erl | 16 ++++++++-------- test/rebar_ct_SUITE.erl | 28 ++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 5fa7ae4..17358a5 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -429,9 +429,9 @@ test_dirs(State, Apps, Opts) -> set_compile_dirs(State, Apps, join(Suites, Dir)); {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} end; - _Specs -> - %% Currently not adding any directories from the spec files. - {ok, rebar_state:project_apps(State, Apps)} + Specs -> + set_compile_dirs(State, Apps, {spec, Specs}) + end. join(Suite, Dir) when is_integer(hd(Suite)) -> @@ -451,15 +451,15 @@ set_compile_dirs(State, Apps, {dir, Dirs}) -> F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), {ok, rebar_state:project_apps(NewState, NewApps)}; -set_compile_dirs(State, Apps, {suite, Suites}) -> - %% suites with dir component - Dirs = find_suite_dirs(Suites), +set_compile_dirs(State, Apps, {Type, Files}) when Type==spec; Type==suite -> + %% specs or suites with dir component + Dirs = find_file_dirs(Files), F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), {ok, rebar_state:project_apps(NewState, NewApps)}. -find_suite_dirs(Suites) -> - AllDirs = lists:map(fun(S) -> filename:dirname(filename:absname(S)) end, Suites), +find_file_dirs(Files) -> + AllDirs = lists:map(fun(F) -> filename:dirname(filename:absname(F)) end, Files), %% eliminate duplicates lists:usort(AllDirs). diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 1995690..d913748 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -1246,9 +1246,15 @@ testspec(Config) -> Name = rebar_test_utils:create_random_name("ct_testspec_"), Vsn = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), - Spec = filename:join([AppDir, "test", "some.spec"]), - ok = filelib:ensure_dir(Spec), - ok = file:write_file(Spec, "[].\n"), + Spec1 = filename:join([AppDir, "test", "some.spec"]), + ok = filelib:ensure_dir(Spec1), + ok = file:write_file(Spec1, "[].\n"), + Spec2 = filename:join([AppDir, "specs", "another.spec"]), + ok = filelib:ensure_dir(Spec2), + ok = file:write_file(Spec2, "[].\n"), + + {ok,Wd} = file:get_cwd(), + ok = file:set_cwd(AppDir), {ok, State} = rebar_test_utils:run_and_check(C, [], @@ -1263,7 +1269,21 @@ testspec(Config) -> {ok, GetOptResult1} = getopt:parse(GetOptSpec, ["--spec","test/some.spec"]), State1 = rebar_state:command_parsed_args(State, GetOptResult1), Tests1 = rebar_prv_common_test:prepare_tests(State1), - {ok, _} = rebar_prv_common_test:compile(State1, Tests1), + {ok, NewState1} = rebar_prv_common_test:compile(State1, Tests1), + [App1] = rebar_state:project_apps(NewState1), + ["test"] = rebar_dir:extra_src_dirs(rebar_app_info:opts(App1)), + + {ok, GetOptResult2} = getopt:parse(GetOptSpec, + ["--spec","specs/another.spec"]), + State2 = rebar_state:command_parsed_args(State, GetOptResult2), + Tests2 = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState2} = rebar_prv_common_test:compile(State1, Tests2), + + [App2] = rebar_state:project_apps(NewState2), + ["specs","test"] = + lists:sort(rebar_dir:extra_src_dirs(rebar_app_info:opts(App2))), + + ok = file:set_cwd(Wd), ok. -- cgit v1.1 From a58857162c3a17356a3d634ac28f0e15c3a1a6f9 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 8 Dec 2016 12:41:22 -0500 Subject: Bump to 3.3.3 --- bootstrap | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index c7f0e06..5359660 100755 --- a/bootstrap +++ b/bootstrap @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.3.2", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.3.3", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence diff --git a/src/rebar.app.src b/src/rebar.app.src index 5b735cf..6f5b6a9 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.3.3"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 51c008fb84ca3313234482be72c80d5aebb0b830 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Thu, 8 Dec 2016 12:42:24 -0500 Subject: Go back to git-based versioning --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index 6f5b6a9..5b735cf 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.3.3"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From a1808f723081f1d7f0161a9ec0277aaf0ef0c8d5 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 8 Dec 2016 22:47:46 +0100 Subject: Update THANKS --- THANKS | 1 + 1 file changed, 1 insertion(+) diff --git a/THANKS b/THANKS index c5f7522..63f4aaa 100644 --- a/THANKS +++ b/THANKS @@ -137,3 +137,4 @@ Stefan Grundmann Carlos Eduardo de Paula Derek Brown Heinz N. Gies +Roberto Aloi -- cgit v1.1 From e427a835301c41a1403cf6b9e6c363ceae3e781c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 9 Dec 2016 14:33:43 +0100 Subject: Translate path to testspec This is a bugfix. It makes sure that the given path to a testspec is translated so common_test will pick the spec from the _build directory, and not from the source tree. --- src/rebar_prv_common_test.erl | 61 ++++++++++++++++++++----------------------- test/rebar_ct_SUITE.erl | 27 ++++++++++++++++--- 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 17358a5..cb33492 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -513,44 +513,39 @@ inject_test_dir(Opts, Dir) -> rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). translate_paths(State, Opts) -> - case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of - {_Suites, undefined} -> translate_suites(State, Opts, []); - {undefined, _Dirs} -> translate_dirs(State, Opts, []); - %% both dirs and suites are defined, only translate dir paths - _ -> translate_dirs(State, Opts, []) + case proplists:get_value(spec, Opts) of + undefined -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + {_Suites, undefined} -> translate_paths(State, suite, Opts, []); + {undefined, _Dirs} -> translate_paths(State, dir, Opts, []); + %% both dirs and suites are defined, only translate dir paths + _ -> translate_paths(State, dir, Opts, []) + end; + _Specs -> + translate_paths(State, spec, Opts, []) end. -translate_dirs(_State, [], Acc) -> lists:reverse(Acc); -translate_dirs(State, [{dir, Dir}|Rest], Acc) when is_integer(hd(Dir)) -> - %% single dir - Apps = rebar_state:project_apps(State), - translate_dirs(State, Rest, [{dir, translate(State, Apps, Dir)}|Acc]); -translate_dirs(State, [{dir, Dirs}|Rest], Acc) -> - %% multiple dirs - Apps = rebar_state:project_apps(State), - NewDirs = {dir, lists:map(fun(Dir) -> translate(State, Apps, Dir) end, Dirs)}, - translate_dirs(State, Rest, [NewDirs|Acc]); -translate_dirs(State, [Test|Rest], Acc) -> - translate_dirs(State, Rest, [Test|Acc]). - -translate_suites(_State, [], Acc) -> lists:reverse(Acc); -translate_suites(State, [{suite, Suite}|Rest], Acc) when is_integer(hd(Suite)) -> - %% single suite +translate_paths(_State, _Type, [], Acc) -> lists:reverse(Acc); +translate_paths(State, Type, [{Type, Val}|Rest], Acc) when is_integer(hd(Val)) -> + %% single file or dir + translate_paths(State, Type, [{Type, [Val]}|Rest], Acc); +translate_paths(State, dir, [{dir, Dirs}|Rest], Acc) -> Apps = rebar_state:project_apps(State), - translate_suites(State, Rest, [{suite, translate_suite(State, Apps, Suite)}|Acc]); -translate_suites(State, [{suite, Suites}|Rest], Acc) -> - %% multiple suites + New = {dir, lists:map(fun(Dir) -> translate(State, Apps, Dir) end, Dirs)}, + translate_paths(State, dir, Rest, [New|Acc]); +translate_paths(State, Type, [{Type, Files}|Rest], Acc) -> + %% Type = suites | specs Apps = rebar_state:project_apps(State), - NewSuites = {suite, lists:map(fun(Suite) -> translate_suite(State, Apps, Suite) end, Suites)}, - translate_suites(State, Rest, [NewSuites|Acc]); -translate_suites(State, [Test|Rest], Acc) -> - translate_suites(State, Rest, [Test|Acc]). - -translate_suite(State, Apps, Suite) -> - Dirname = filename:dirname(Suite), - Basename = filename:basename(Suite), + New = {Type, lists:map(fun(File) -> translate_file(State, Apps, File) end, Files)}, + translate_paths(State, Type, Rest, [New|Acc]); +translate_paths(State, Type, [Test|Rest], Acc) -> + translate_paths(State, Type, Rest, [Test|Acc]). + +translate_file(State, Apps, File) -> + Dirname = filename:dirname(File), + Basename = filename:basename(File), case Dirname of - "." -> Suite; + "." -> File; _ -> filename:join([translate(State, Apps, Dirname), Basename]) end. diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index d913748..6339e6b 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -1248,10 +1248,10 @@ testspec(Config) -> rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), Spec1 = filename:join([AppDir, "test", "some.spec"]), ok = filelib:ensure_dir(Spec1), - ok = file:write_file(Spec1, "[].\n"), + ok = file:write_file(Spec1, "{suites,\".\",all}.\n"), Spec2 = filename:join([AppDir, "specs", "another.spec"]), ok = filelib:ensure_dir(Spec2), - ok = file:write_file(Spec2, "[].\n"), + ok = file:write_file(Spec2, "{suites,\"../test/\",all}.\n"), {ok,Wd} = file:get_cwd(), ok = file:set_cwd(AppDir), @@ -1266,23 +1266,42 @@ testspec(Config) -> CommandProvider = providers:get_provider(ct, Providers, Namespace), GetOptSpec = providers:opts(CommandProvider), + %% Testspec in "test" directory {ok, GetOptResult1} = getopt:parse(GetOptSpec, ["--spec","test/some.spec"]), State1 = rebar_state:command_parsed_args(State, GetOptResult1), Tests1 = rebar_prv_common_test:prepare_tests(State1), {ok, NewState1} = rebar_prv_common_test:compile(State1, Tests1), + {ok, T1} = Tests1, + Opts1= rebar_prv_common_test:translate_paths(NewState1, T1), + + %% check thath extra src dir is added [App1] = rebar_state:project_apps(NewState1), ["test"] = rebar_dir:extra_src_dirs(rebar_app_info:opts(App1)), + %% check that path is translated + ExpectedSpec1 = filename:join([AppDir, "_build", "test", "lib", Name, + "test", "some.spec"]), + [ExpectedSpec1] = proplists:get_value(spec, Opts1), + + + %% Testspec in directory other than "test" {ok, GetOptResult2} = getopt:parse(GetOptSpec, ["--spec","specs/another.spec"]), State2 = rebar_state:command_parsed_args(State, GetOptResult2), - Tests2 = rebar_prv_common_test:prepare_tests(State2), - {ok, NewState2} = rebar_prv_common_test:compile(State1, Tests2), + Tests2 = {ok, T2} =rebar_prv_common_test:prepare_tests(State2), + {ok, NewState2} = rebar_prv_common_test:compile(State2, Tests2), + Opts2= rebar_prv_common_test:translate_paths(NewState2, T2), + %% check thath extra src dirs are added [App2] = rebar_state:project_apps(NewState2), ["specs","test"] = lists:sort(rebar_dir:extra_src_dirs(rebar_app_info:opts(App2))), + %% check that paths are translated + ExpectedSpec2 = filename:join([AppDir, "_build", "test", "lib", Name, + "specs", "another.spec"]), + [ExpectedSpec2] = proplists:get_value(spec, Opts2), + ok = file:set_cwd(Wd), ok. -- cgit v1.1 From fc12b06d27d0e1df4719a2e38e241f36637d369e Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 9 Dec 2016 09:16:15 -0500 Subject: Fix regex match for ignored file The regex mistakenly matched too many files (any character followed by an underscore) rather than only files starting in '._' This properly escapes the expressions to work in all cases. --- src/rebar_base_compiler.erl | 2 +- src/rebar_erlc_compiler.erl | 2 +- src/rebar_prv_eunit.erl | 2 +- src/rebar_templater.erl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index bfd79a5..9aa7419 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -54,7 +54,7 @@ run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, Opts) -> %% Convert simple extension to proper regex - SourceExtRe = "^(?!._).*\\" ++ SourceExt ++ [$$], + SourceExtRe = "^(?!\\._).*\\" ++ SourceExt ++ [$$], Recursive = proplists:get_value(recursive, Opts, true), %% Find all possible source files diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index d4d257f..95573fd 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -48,7 +48,7 @@ -type compile_opt() :: {recursive, boolean()}. -define(DEFAULT_OUTDIR, "ebin"). --define(RE_PREFIX, "^(?!._)"). +-define(RE_PREFIX, "^(?!\\._)"). %% =================================================================== %% Public API diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 6fdf33e..a9db12e 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -18,7 +18,7 @@ %% we need to modify app_info state before compile -define(DEPS, [lock]). --define(DEFAULT_TEST_REGEX, "^(?!._).*\\.erl\$"). +-define(DEFAULT_TEST_REGEX, "^(?!\\._).*\\.erl\$"). %% =================================================================== %% Public API diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 7e0aae4..e64ce71 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -33,7 +33,7 @@ -include("rebar.hrl"). --define(TEMPLATE_RE, "^(?!._).*\\.template\$"). +-define(TEMPLATE_RE, "^(?!\\._).*\\.template\$"). %% =================================================================== %% Public API -- cgit v1.1 From 5b9bd7a10283fba4c3bac5a153b3937a73492752 Mon Sep 17 00:00:00 2001 From: Artem Teslenko Date: Sat, 10 Dec 2016 17:35:30 +0200 Subject: Add rebar3.crashdump to gitignore template --- priv/templates/gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/templates/gitignore b/priv/templates/gitignore index 121a4de..468614d 100644 --- a/priv/templates/gitignore +++ b/priv/templates/gitignore @@ -14,3 +14,4 @@ erl_crash.dump logs _build .idea +rebar3.crashdump -- cgit v1.1 From 6466c324493366cc8c01ee27c08a959e272a4c2c Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Sun, 11 Dec 2016 17:55:05 +0000 Subject: shell: don't crash apps that use release version operators Like for instance: {app, "0.1.0", '='} --- src/rebar_prv_shell.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 31b2e17..0a3e18a 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -352,11 +352,15 @@ boot_apps(Apps) -> normalize_load_apps([]) -> []; normalize_load_apps([{App, _} | T]) -> [App | normalize_load_apps(T)]; normalize_load_apps([{App, _Vsn, load} | T]) -> [App | normalize_load_apps(T)]; +normalize_load_apps([{App, _Vsn, Operator} | T]) when is_atom(Operator) -> + [App | normalize_load_apps(T)]; normalize_load_apps([App | T]) when is_atom(App) -> [App | normalize_load_apps(T)]. normalize_boot_apps([]) -> []; normalize_boot_apps([{_App, load} | T]) -> normalize_boot_apps(T); normalize_boot_apps([{_App, _Vsn, load} | T]) -> normalize_boot_apps(T); +normalize_boot_apps([{App, _Vsn, Operator} | T]) when is_atom(Operator) -> + [App | normalize_boot_apps(T)]; normalize_boot_apps([{App, _Vsn} | T]) -> [App | normalize_boot_apps(T)]; normalize_boot_apps([App | T]) when is_atom(App) -> [App | normalize_boot_apps(T)]. -- cgit v1.1 From a18340c6eed6e62237c4ec63f15daacfe86dc86c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 13 Dec 2016 12:51:01 +0100 Subject: Allow using relative path to suite in project root --- src/rebar_prv_common_test.erl | 15 +----------- test/rebar_ct_SUITE.erl | 55 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index cb33492..9db8106 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -529,26 +529,13 @@ translate_paths(_State, _Type, [], Acc) -> lists:reverse(Acc); translate_paths(State, Type, [{Type, Val}|Rest], Acc) when is_integer(hd(Val)) -> %% single file or dir translate_paths(State, Type, [{Type, [Val]}|Rest], Acc); -translate_paths(State, dir, [{dir, Dirs}|Rest], Acc) -> - Apps = rebar_state:project_apps(State), - New = {dir, lists:map(fun(Dir) -> translate(State, Apps, Dir) end, Dirs)}, - translate_paths(State, dir, Rest, [New|Acc]); translate_paths(State, Type, [{Type, Files}|Rest], Acc) -> - %% Type = suites | specs Apps = rebar_state:project_apps(State), - New = {Type, lists:map(fun(File) -> translate_file(State, Apps, File) end, Files)}, + New = {Type, lists:map(fun(File) -> translate(State, Apps, File) end, Files)}, translate_paths(State, Type, Rest, [New|Acc]); translate_paths(State, Type, [Test|Rest], Acc) -> translate_paths(State, Type, Rest, [Test|Acc]). -translate_file(State, Apps, File) -> - Dirname = filename:dirname(File), - Basename = filename:basename(File), - case Dirname of - "." -> File; - _ -> filename:join([translate(State, Apps, Dirname), Basename]) - end. - translate(State, [App|Rest], Path) -> case rebar_file_utils:path_from_ancestor(Path, rebar_app_info:dir(App)) of {ok, P} -> filename:join([rebar_app_info:out_dir(App), P]); diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 6339e6b..a47ea58 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -693,7 +693,33 @@ suite_at_root(Config) -> true = filelib:is_dir(DataDir), DataFile = filename:join([AppDir, "_build", "test", "extras", "root_SUITE_data", "some_data.txt"]), - true = filelib:is_file(DataFile). + true = filelib:is_file(DataFile), + + %% Same test again, but using relative path to the suite from the + %% project root + {ok,Cwd} = file:get_cwd(), + ok = file:set_cwd(AppDir), + rebar_file_utils:rm_rf("_build"), + + {ok, GetOptResult2} = getopt:parse(GetOptSpec, ["--suite=" ++ "root_SUITE"]), + + State3 = rebar_state:command_parsed_args(State1, GetOptResult2), + + Tests2 = rebar_prv_common_test:prepare_tests(State3), + {ok, NewState2} = rebar_prv_common_test:compile(State3, Tests2), + {ok, T2} = Tests2, + Opts2 = rebar_prv_common_test:translate_paths(NewState2, T2), + + ok = file:set_cwd(Cwd), + + Suite2 = proplists:get_value(suite, Opts2), + [Expected] = Suite2, + true = filelib:is_file(TestHrl), + true = filelib:is_file(TestBeam), + true = filelib:is_dir(DataDir), + true = filelib:is_file(DataFile), + + ok. suite_at_app_root(Config) -> AppDir = ?config(apps, Config), @@ -730,7 +756,32 @@ suite_at_app_root(Config) -> true = filelib:is_dir(DataDir), DataFile = filename:join([AppDir, "_build", "test", "lib", Name2, "app_root_SUITE_data", "some_data.txt"]), - true = filelib:is_file(DataFile). + true = filelib:is_file(DataFile), + + %% Same test again using relative path to the suite from the project root + {ok,Cwd} = file:get_cwd(), + ok = file:set_cwd(AppDir), + rebar_file_utils:rm_rf("_build"), + + {ok, GetOptResult2} = getopt:parse(GetOptSpec, ["--suite=" ++ filename:join(["apps", Name2, "app_root_SUITE"])]), + + State3 = rebar_state:command_parsed_args(State1, GetOptResult2), + + Tests2 = rebar_prv_common_test:prepare_tests(State3), + {ok, NewState2} = rebar_prv_common_test:compile(State3, Tests2), + {ok, T2} = Tests2, + Opts2 = rebar_prv_common_test:translate_paths(NewState2, T2), + + ok = file:set_cwd(Cwd), + + Suite2 = proplists:get_value(suite, Opts2), + [Expected] = Suite2, + true = filelib:is_file(TestHrl), + true = filelib:is_file(TestBeam), + true = filelib:is_dir(DataDir), + true = filelib:is_file(DataFile), + + ok. %% this test probably only fails when this suite is run via rebar3 with the --cover flag data_dir_correct(Config) -> -- cgit v1.1 From b99c15d29755efcb2a573ddbd9eb9cd2d42dc054 Mon Sep 17 00:00:00 2001 From: getong Date: Wed, 14 Dec 2016 20:09:02 +0800 Subject: the releases website has changed --- src/rebar_prv_shell.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 31b2e17..7d1ee07 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -339,7 +339,7 @@ reread_config(State) -> boot_apps(Apps) -> ?WARN("The rebar3 shell is a development tool; to deploy " "applications in production, consider using releases " - "(http://www.rebar3.org/v3.0/docs/releases)", []), + "(http://www.rebar3.org/docs/releases)", []), Normalized = normalize_boot_apps(Apps), Res = [application:ensure_all_started(App) || App <- Normalized], _ = [?INFO("Booted ~p", [App]) -- cgit v1.1 From 8ae17c483d86f151d69091546b3577381662e27e Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 16 Dec 2016 21:02:55 -0500 Subject: Fix Alisdair's review, add more types and docs --- src/rebar_app_discover.erl | 6 ++- src/rebar_config.erl | 6 +-- src/rebar_digraph.erl | 22 ++++++++++- src/rebar_dir.erl | 91 +++++++++++++++++++++++++++++++++++++--------- src/rebar_dist_utils.erl | 9 +++++ 5 files changed, 109 insertions(+), 25 deletions(-) diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index acefdb4..fd55960 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -13,7 +13,9 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). -%% @doc from the base directory, +%% @doc from the base directory, find all the applications +%% at the top level and their dependencies based on the configuration +%% and profile information. -spec do(rebar_state:t(), [file:filename()]) -> rebar_state:t(). do(State, LibDirs) -> BaseDir = rebar_state:dir(State), @@ -158,7 +160,7 @@ project_app_config(AppInfo, State) -> Opts = maybe_reset_hooks(Dir, rebar_state:opts(State), State), {C, rebar_state:opts(State, Opts)}. -%% @doc Here we check if the app is at the root of the project. +%% @private Check if the app is at the root of the project. %% If it is, then drop the hooks from the config so they aren't run twice -spec maybe_reset_hooks(file:filename(), Opts, rebar_state:t()) -> Opts when Opts :: rebar_dict(). diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 84f40d4..97e27ab 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -125,9 +125,9 @@ write_lock_file(LockFile, Locks) -> format_attrs(Attrs)])) end. -%% @private Attributes have a special formatting to ensure there's only one per -%% line in terms of pkg_hash, so we disturb source diffing as little -%% as possible. +%% @private Because attributes for packages are fairly large, there is the need +%% for a special formatting to ensure there's only one entry per lock file +%% line and that diffs are generally stable. -spec format_attrs([term()]) -> iodata(). format_attrs([]) -> []; format_attrs([{pkg_hash, Vals}|T]) -> diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl index 363253a..a827735 100644 --- a/src/rebar_digraph.erl +++ b/src/rebar_digraph.erl @@ -1,3 +1,5 @@ +%%% @doc build a digraph of applications in order to figure out dependency +%%% and compile order. -module(rebar_digraph). -export([compile_order/1 @@ -7,7 +9,9 @@ -include("rebar.hrl"). -%% Sort apps with topological sort to get proper build order +%% @doc Sort apps with topological sort to get proper build order +-spec compile_order([rebar_app_info:t()]) -> + {ok, [rebar_app_info:t()]} | {error, no_sort | {cycles, [[binary(),...]]}}. compile_order(Apps) -> Graph = digraph:new(), lists:foreach(fun(App) -> @@ -33,6 +37,11 @@ compile_order(Apps) -> true = digraph:delete(Graph), Order. +%% @private Add a package and its dependencies to an existing digraph +-spec add(digraph:graph(), {PkgName, [Dep]}) -> ok when + PkgName :: binary(), + Dep :: {Name, term()} | Name, + Name :: atom() | iodata(). add(Graph, {PkgName, Deps}) -> case digraph:vertex(Graph, PkgName) of false -> @@ -57,6 +66,8 @@ add(Graph, {PkgName, Deps}) -> digraph:add_edge(Graph, V, V3) end, Deps). +%% @doc based on a list of vertices and edges, build a digraph. +-spec restore_graph({[digraph:vertex()], [digraph:edge()]}) -> digraph:graph(). restore_graph({Vs, Es}) -> Graph = digraph:new(), lists:foreach(fun({V, LastUpdated}) -> @@ -67,6 +78,8 @@ restore_graph({Vs, Es}) -> end, Es), Graph. +%% @doc convert a given exception's payload into an io description. +-spec format_error(any()) -> iolist(). format_error(no_solution) -> io_lib:format("No solution for packages found.", []). @@ -74,22 +87,27 @@ format_error(no_solution) -> %% Internal Functions %%==================================================================== +%% @doc alias for `digraph_utils:subgraph/2'. subgraph(Graph, Vertices) -> digraph_utils:subgraph(Graph, Vertices). +%% @private from a list of app names, fetch the proper app info records +%% for them. -spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()]. names_to_apps(Names, Apps) -> [element(2, App) || App <- [find_app_by_name(Name, Apps) || Name <- Names], App =/= error]. +%% @private fetch the proper app info record for a given app name. -spec find_app_by_name(atom(), [rebar_app_info:t()]) -> {ok, rebar_app_info:t()} | error. find_app_by_name(Name, Apps) -> ec_lists:find(fun(App) -> rebar_app_info:name(App) =:= Name end, Apps). -%% The union of all entries in the applications list for an app and +%% @private The union of all entries in the applications list for an app and %% the deps listed in its rebar.config is all deps that may be needed %% for building the app. +-spec all_apps_deps(rebar_app_info:t()) -> [binary()]. all_apps_deps(App) -> Applications = lists:usort([atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)]), Deps = lists:usort(lists:map(fun({Name, _}) -> Name; (Name) -> Name end, rebar_app_info:deps(App))), diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index 32cb264..069d8fd 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -1,3 +1,4 @@ +%%% @doc utility functions for directory and path handling of all kind. -module(rebar_dir). -export([base_dir/1, @@ -29,10 +30,14 @@ -include("rebar.hrl"). +%% @doc returns the directory root for build artifacts +%% for the current profile, such as `_build/default/'. -spec base_dir(rebar_state:t()) -> file:filename_all(). base_dir(State) -> profile_dir(rebar_state:opts(State), rebar_state:current_profiles(State)). +%% @doc returns the directory root for build artifacts for a given set +%% of profiles. -spec profile_dir(rebar_dict(), [atom()]) -> file:filename_all(). profile_dir(Opts, Profiles) -> {BaseDir, ProfilesStrings} = case [ec_cnv:to_list(P) || P <- Profiles] of @@ -46,25 +51,36 @@ profile_dir(Opts, Profiles) -> ProfilesDir = string:join(ProfilesStrings, "+"), filename:join(BaseDir, ProfilesDir). +%% @doc returns the directory where dependencies should be placed +%% given the current profile. -spec deps_dir(rebar_state:t()) -> file:filename_all(). deps_dir(State) -> filename:join(base_dir(State), rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR)). +%% @doc returns the directory where a dependency should be placed +%% given the current profile, based on its app name. Expects to be passed +%% the result of `deps_dir/1' as a first argument. -spec deps_dir(file:filename_all(), file:filename_all()) -> file:filename_all(). deps_dir(DepsDir, App) -> filename:join(DepsDir, App). +%% @doc returns the absolute path for the project root (by default, +%% the current working directory for the currently running escript). root_dir(State) -> filename:absname(rebar_state:get(State, root_dir, ?DEFAULT_ROOT_DIR)). +%% @doc returns the expected location of the `_checkouts' directory. -spec checkouts_dir(rebar_state:t()) -> file:filename_all(). checkouts_dir(State) -> filename:join(root_dir(State), rebar_state:get(State, checkouts_dir, ?DEFAULT_CHECKOUTS_DIR)). +%% @doc returns the expected location of a given app in the checkouts +%% directory for the project. -spec checkouts_dir(rebar_state:t(), file:filename_all()) -> file:filename_all(). checkouts_dir(State, App) -> filename:join(checkouts_dir(State), App). +%% @doc Returns the directory where plugins are located. -spec plugins_dir(rebar_state:t()) -> file:filename_all(). plugins_dir(State) -> case lists:member(global, rebar_state:current_profiles(State)) of @@ -74,33 +90,50 @@ plugins_dir(State) -> filename:join(base_dir(State), rebar_state:get(State, plugins_dir, ?DEFAULT_PLUGINS_DIR)) end. +%% @doc returns the list of relative path where the project applications can +%% be located. -spec lib_dirs(rebar_state:t()) -> file:filename_all(). lib_dirs(State) -> rebar_state:get(State, project_app_dirs, ?DEFAULT_PROJECT_APP_DIRS). +%% @doc returns the user's home directory. +-spec home_dir() -> file:filename_all(). home_dir() -> {ok, [[Home]]} = init:get_argument(home), Home. +%% @doc returns the directory where the global configuration files for rebar3 +%% may be stored. +-spec global_config_dir(rebar_state:t()) -> file:filename_all(). global_config_dir(State) -> Home = home_dir(), rebar_state:get(State, global_rebar_dir, filename:join([Home, ".config", "rebar3"])). +%% @doc returns the path of the global rebar.config file +-spec global_config(rebar_state:t()) -> file:filename_all(). global_config(State) -> filename:join(global_config_dir(State), "rebar.config"). +%% @doc returns the default path of the global rebar.config file +-spec global_config() -> file:filename_all(). global_config() -> Home = home_dir(), filename:join([Home, ".config", "rebar3", "rebar.config"]). +%% @doc returns the location for the global cache directory -spec global_cache_dir(rebar_dict()) -> file:filename_all(). global_cache_dir(Opts) -> Home = home_dir(), rebar_opts:get(Opts, global_rebar_dir, filename:join([Home, ".cache", "rebar3"])). +%% @doc appends the cache directory to the path passed to this function. +-spec local_cache_dir(file:filename_all()) -> file:filename_all(). local_cache_dir(Dir) -> filename:join(Dir, ".rebar3"). +%% @doc returns the current working directory, with some specific +%% conversions and handling done to be cross-platform compatible. +-spec get_cwd() -> file:filename_all(). get_cwd() -> {ok, Dir} = file:get_cwd(), %% On windows cwd may return capital letter for drive, @@ -109,9 +142,14 @@ get_cwd() -> %% cwd as soon as it possible. filename:join([Dir]). +%% @doc returns the file location for the global template +%% configuration variables file. +-spec template_globals(rebar_state:t()) -> file:filename_all(). template_globals(State) -> filename:join([global_config_dir(State), "templates", "globals"]). +%% @doc returns the location for the global template directory +-spec template_dir(rebar_state:t()) -> file:filename_all(). template_dir(State) -> filename:join([global_config_dir(State), "templates"]). @@ -129,6 +167,8 @@ processing_base_dir(State, Dir) -> AbsDir = filename:absname(Dir), AbsDir =:= rebar_state:get(State, base_dir). +%% @doc make a path absolute +-spec make_absolute_path(file:filename()) -> file:filename(). make_absolute_path(Path) -> case filename:pathtype(Path) of absolute -> @@ -142,11 +182,16 @@ make_absolute_path(Path) -> filename:join([Dir, Path]) end. +%% @doc normalizing a path removes all of the `..' and the +%% `.' segments it may contain. +-spec make_normalized_path(file:filename()) -> file:filename(). make_normalized_path(Path) -> AbsPath = make_absolute_path(Path), Components = filename:split(AbsPath), make_normalized_path(Components, []). +%% @private drops path fragments for normalization +-spec make_normalized_path([string()], [string()]) -> file:filename(). make_normalized_path([], NormalizedPath) -> filename:join(lists:reverse(NormalizedPath)); make_normalized_path([H|T], NormalizedPath) -> @@ -181,37 +226,42 @@ do_make_relative_path(Source, Target) -> Base = lists:duplicate(max(length(Target) - 1, 0), ".."), filename:join(Base ++ Source). -%%%----------------------------------------------------------------- -%%% 'src_dirs' and 'extra_src_dirs' can be configured with options +%%% @doc +%%% `src_dirs' and `extra_src_dirs' can be configured with options %%% like this: -%%% +%%% ``` %%% {src_dirs,[{"foo",[{recursive,false}]}]} %%% {extra_src_dirs,[{"bar",[recursive]}]} (equivalent to {recursive,true}) -%%% -%%% src_dirs/1,2 and extra_src_dirs/1,2 return only the list of -%%% directories for the 'src_dirs' and 'extra_src_dirs' options -%%% respectively, while src_dirs_opts/2 return the options list for -%%% the given directory, no matter if it is configured as 'src_dirs' or -%%% 'extra_src_dirs'. -%%% +%%% ''' +%%% `src_dirs/1,2' and `extra_src_dirs/1,2' return only the list of +%%% directories for the `src_dirs' and `extra_src_dirs' options +%%% respectively, while `src_dirs_opts/2' returns the options list for +%%% the given directory, no matter if it is configured as `src_dirs' or +%%% `extra_src_dirs'. -spec src_dirs(rebar_dict()) -> list(file:filename_all()). src_dirs(Opts) -> src_dirs(Opts, []). +%% @doc same as `src_dirs/1', but allows to pass in a list of default options. -spec src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()). src_dirs(Opts, Default) -> src_dirs(src_dirs, Opts, Default). +%% @doc same as `src_dirs/1', but for the `extra_src_dirs' options -spec extra_src_dirs(rebar_dict()) -> list(file:filename_all()). extra_src_dirs(Opts) -> extra_src_dirs(Opts, []). +%% @doc same as `src_dirs/2', but for the `extra_src_dirs' options -spec extra_src_dirs(rebar_dict(), list(file:filename_all())) -> list(file:filename_all()). extra_src_dirs(Opts, Default) -> src_dirs(extra_src_dirs, Opts, Default). +%% @private agnostic version of src_dirs and extra_src_dirs. src_dirs(Type, Opts, Default) -> lists:usort([case D0 of {D,_} -> D; _ -> D0 end || D0 <- raw_src_dirs(Type,Opts,Default)]). +%% @private extracts the un-formatted src_dirs or extra_src_dirs +%% options as configured. raw_src_dirs(Type, Opts, Default) -> ErlOpts = rebar_opts:erl_opts(Opts), Vs = proplists:get_all_values(Type, ErlOpts), @@ -220,19 +270,23 @@ raw_src_dirs(Type, Opts, Default) -> Dirs -> Dirs end. +%% @doc returns all the source directories (`src_dirs' and +%% `extra_src_dirs'). -spec all_src_dirs(rebar_dict()) -> list(file:filename_all()). all_src_dirs(Opts) -> all_src_dirs(Opts, [], []). +%% @doc returns all the source directories (`src_dirs' and +%% `extra_src_dirs') while being able to configure defaults for both. -spec all_src_dirs(rebar_dict(), list(file:filename_all()), list(file:filename_all())) -> list(file:filename_all()). all_src_dirs(Opts, SrcDefault, ExtraDefault) -> lists:usort(src_dirs(Opts, SrcDefault) ++ extra_src_dirs(Opts, ExtraDefault)). -%%%----------------------------------------------------------------- +%%% @doc %%% Return the list of options for the given src directory %%% If the same option is given multiple times for a directory in the -%%% config, the priority order is: first occurence of 'src_dirs' -%%% followed by first occurence of 'extra_src_dirs'. +%%% config, the priority order is: first occurence of `src_dirs' +%%% followed by first occurence of `extra_src_dirs'. -spec src_dir_opts(rebar_dict(), file:filename_all()) -> [{atom(),term()}]. src_dir_opts(Opts, Dir) -> RawSrcDirs = raw_src_dirs(src_dirs, Opts, []), @@ -241,7 +295,7 @@ src_dir_opts(Opts, Dir) -> D==Dir], lists:ukeysort(1,proplists:unfold(lists:append(AllOpts))). -%%%----------------------------------------------------------------- +%%% @doc %%% Return the value of the 'recursive' option for the given directory. %%% If not given, the value of 'recursive' in the 'erlc_compiler' %%% options is used, and finally the default is 'true'. @@ -254,16 +308,17 @@ recursive(Opts, Dir) -> R = proplists:get_value(recursive, DirOpts, Default), R. -%% given a path if that path is an ancestor of an app dir return the path relative to that -%% apps outdir. if the path is not an ancestor to any app dirs but is an ancestor of the -%% project root return the path relative to the project base_dir. if it is not an ancestor +%% @doc given a path if that path is an ancestor of an app dir, return the path relative to that +%% apps outdir. If the path is not an ancestor to any app dirs but is an ancestor of the +%% project root, return the path relative to the project base_dir. If it is not an ancestor %% of either return it unmodified -spec retarget_path(rebar_state:t(), string()) -> string(). - retarget_path(State, Path) -> ProjectApps = rebar_state:project_apps(State), retarget_path(State, Path, ProjectApps). +%% @private worker for retarget_path/2 +%% @end %% not relative to any apps in project, check to see it's relative to %% project root retarget_path(State, Path, []) -> diff --git a/src/rebar_dist_utils.erl b/src/rebar_dist_utils.erl index 03f4392..93edf9d 100644 --- a/src/rebar_dist_utils.erl +++ b/src/rebar_dist_utils.erl @@ -7,6 +7,9 @@ %%%%%%%%%%%%%%%%%% %%% PUBLIC API %%% %%%%%%%%%%%%%%%%%% + +%% @doc allows to pick whether to use a short or long name, and +%% starts the distributed mode for it. -spec either(Name::atom(), SName::atom(), Opts::[{setcookie,term()}]) -> atom(). either(undefined, undefined, _) -> 'nonode@nohost'; @@ -19,12 +22,18 @@ either(undefined, SName, Opts) -> either(_, _, _) -> ?ABORT("Cannot have both short and long node names defined", []). +%% @doc starts a node with a short name. +-spec short(SName::atom(), Opts::[{setcookie,term()}]) -> term(). short(Name, Opts) -> start(Name, shortnames, Opts). +%% @doc starts a node with a long name. +-spec long(Name::atom(), Opts::[{setcookie,term()}]) -> term(). long(Name, Opts) -> start(Name, longnames, Opts). +%% @doc utility function to extract all distribution options +%% from a rebar3 state tuple. -spec find_options(rebar_state:t()) -> {Long, Short, Opts} when Long :: atom(), Short :: atom(), -- cgit v1.1 From 4e52ce58e505ba57f2188e34a002d70de1bd6ff5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 16 Dec 2016 10:34:36 +0100 Subject: Add all dirs from test spec Parse given test specs and add all spec- and suite directories as extra_src_dirs in order to ensure that all these directories are copied to the _build area and the suites are compiled. Specs located in the project- or app root are explicitly copied to the _build area in order to avoid recursive copying of the complete directory tree. --- src/rebar_prv_common_test.erl | 81 ++++++++++++++++++++++++++++----- test/rebar_ct_SUITE.erl | 103 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 168 insertions(+), 16 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 9db8106..4a9ba25 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -429,9 +429,18 @@ test_dirs(State, Apps, Opts) -> set_compile_dirs(State, Apps, join(Suites, Dir)); {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} end; - Specs -> - set_compile_dirs(State, Apps, {spec, Specs}) - + Specs0 -> + case get_dirs_from_specs(Specs0) of + {ok,{Specs,SuiteDirs}} -> + {State1,Apps1} = set_compile_dirs1(State, Apps, + {dir, SuiteDirs}), + {State2,Apps2} = set_compile_dirs1(State1, Apps1, + {spec, Specs}), + [maybe_copy_spec(State2,Apps2,S) || S <- Specs], + {ok, rebar_state:project_apps(State2, Apps2)}; + Error -> + Error + end end. join(Suite, Dir) when is_integer(hd(Suite)) -> @@ -439,24 +448,25 @@ join(Suite, Dir) when is_integer(hd(Suite)) -> join(Suites, Dir) -> {suite, lists:map(fun(S) -> filename:join([Dir, S]) end, Suites)}. -set_compile_dirs(State, Apps, {dir, Dir}) when is_integer(hd(Dir)) -> +set_compile_dirs(State, Apps, What) -> + {NewState,NewApps} = set_compile_dirs1(State, Apps, What), + {ok, rebar_state:project_apps(NewState, NewApps)}. + +set_compile_dirs1(State, Apps, {dir, Dir}) when is_integer(hd(Dir)) -> %% single directory %% insert `Dir` into an app if relative, or the base state if not %% app relative but relative to the root or not at all if outside %% project scope - {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), - {ok, rebar_state:project_apps(NewState, NewApps)}; -set_compile_dirs(State, Apps, {dir, Dirs}) -> + maybe_inject_test_dir(State, [], Apps, Dir); +set_compile_dirs1(State, Apps, {dir, Dirs}) -> %% multiple directories F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, - {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), - {ok, rebar_state:project_apps(NewState, NewApps)}; -set_compile_dirs(State, Apps, {Type, Files}) when Type==spec; Type==suite -> + lists:foldl(F, {State, Apps}, Dirs); +set_compile_dirs1(State, Apps, {Type, Files}) when Type==spec; Type==suite -> %% specs or suites with dir component Dirs = find_file_dirs(Files), F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, - {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), - {ok, rebar_state:project_apps(NewState, NewApps)}. + lists:foldl(F, {State, Apps}, Dirs). find_file_dirs(Files) -> AllDirs = lists:map(fun(F) -> filename:dirname(filename:absname(F)) end, Files), @@ -507,11 +517,58 @@ copy_bare_suites(From, To) -> ok = rebar_file_utils:cp_r(SrcFiles, To), rebar_file_utils:cp_r(DataDirs, To). +maybe_copy_spec(State, [App|Apps], Spec) -> + case rebar_file_utils:path_from_ancestor(filename:dirname(Spec), rebar_app_info:dir(App)) of + {ok, []} -> + ok = rebar_file_utils:cp_r([Spec],rebar_app_info:out_dir(App)); + {ok,_} -> + ok; + {error,badparent} -> + maybe_copy_spec(State, Apps, Spec) + end; +maybe_copy_spec(State, [], Spec) -> + case rebar_file_utils:path_from_ancestor(filename:dirname(Spec), rebar_state:dir(State)) of + {ok, []} -> + ExtrasDir = filename:join([rebar_dir:base_dir(State), "extras"]), + ok = rebar_file_utils:cp_r([Spec],ExtrasDir); + _R -> + ok + end. + inject_test_dir(Opts, Dir) -> %% append specified test targets to app defined `extra_src_dirs` ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). +get_dirs_from_specs(Specs) -> + case get_tests_from_specs(Specs) of + {ok,Tests} -> + {SpecLists,NodeRunSkipLists} = lists:unzip(Tests), + SpecList = lists:append(SpecLists), + NodeRunSkipList = lists:append(NodeRunSkipLists), + RunList = lists:append([R || {_,R,_} <- NodeRunSkipList]), + DirList = [element(1,R) || R <- RunList], + {ok,{SpecList,DirList}}; + Error -> + Error + end. + +get_tests_from_specs(Specs) -> + case erlang:function_exported(ct_testspec,get_tests,1) of + true -> + ct_testspec:get_tests(Specs); + false -> + case ct_testspec:collect_tests_from_file(Specs,true) of + Tests when is_list(Tests) -> + {ok,[{S,ct_testspec:prepare_tests(R)} || {S,R} <- Tests]}; + R when is_tuple(R), element(1,R)==testspec -> + %% R15 + {ok,[{Specs,ct_testspec:prepare_tests(R)}]}; + Error -> + Error + end + end. + translate_paths(State, Opts) -> case proplists:get_value(spec, Opts) of undefined -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index a47ea58..91324d2 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -53,6 +53,7 @@ misspecified_ct_compile_opts/1, misspecified_ct_first_files/1, testspec/1, + testspec_at_root/1, cmd_vs_cfg_opts/1]). -include_lib("eunit/include/eunit.hrl"). @@ -71,6 +72,7 @@ all() -> [{group, basic_app}, misspecified_ct_compile_opts, misspecified_ct_first_files, testspec, + testspec_at_root, cmd_vs_cfg_opts]. groups() -> [{basic_app, [], [basic_app_default_dirs, @@ -1302,7 +1304,9 @@ testspec(Config) -> ok = file:write_file(Spec1, "{suites,\".\",all}.\n"), Spec2 = filename:join([AppDir, "specs", "another.spec"]), ok = filelib:ensure_dir(Spec2), - ok = file:write_file(Spec2, "{suites,\"../test/\",all}.\n"), + Suites2 = filename:join([AppDir,"suites","*"]), + ok = filelib:ensure_dir(Suites2), + ok = file:write_file(Spec2, "{suites,\"../suites/\",all}.\n"), {ok,Wd} = file:get_cwd(), ok = file:set_cwd(AppDir), @@ -1325,7 +1329,7 @@ testspec(Config) -> {ok, T1} = Tests1, Opts1= rebar_prv_common_test:translate_paths(NewState1, T1), - %% check thath extra src dir is added + %% check that extra src dir is added [App1] = rebar_state:project_apps(NewState1), ["test"] = rebar_dir:extra_src_dirs(rebar_app_info:opts(App1)), @@ -1343,9 +1347,9 @@ testspec(Config) -> {ok, NewState2} = rebar_prv_common_test:compile(State2, Tests2), Opts2= rebar_prv_common_test:translate_paths(NewState2, T2), - %% check thath extra src dirs are added + %% check that extra src dirs are added [App2] = rebar_state:project_apps(NewState2), - ["specs","test"] = + ["specs","suites","test"] = lists:sort(rebar_dir:extra_src_dirs(rebar_app_info:opts(App2))), %% check that paths are translated @@ -1357,6 +1361,97 @@ testspec(Config) -> ok. +testspec_at_root(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_testspec_at_root_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_testspec_at_root_"), + Vsn = rebar_test_utils:create_random_vsn(), + AppDir1 = filename:join([AppDir, "apps", Name]), + rebar_test_utils:create_app(AppDir1, Name, Vsn, [kernel, stdlib]), + + Spec1 = filename:join([AppDir, "root.spec"]), + ok = filelib:ensure_dir(Spec1), + ok = file:write_file(Spec1, "{suites,\"test\",all}."), + Spec2 = filename:join([AppDir, "root1.spec"]), + ok = file:write_file(Spec2, "{suites,\".\",all}."), + Spec3 = filename:join([AppDir, "root2.spec"]), + ok = file:write_file(Spec3, "{suites,\"suites\",all}."), + Suite1 = filename:join(AppDir,"root_SUITE.erl"), + ok = file:write_file(Suite1, test_suite("root")), + Suite2 = filename:join([AppDir,"suites","test_SUITE.erl"]), + ok = filelib:ensure_dir(Suite2), + ok = file:write_file(Suite2, test_suite("test")), + + {ok, State} = rebar_test_utils:run_and_check(C, + [], + ["as", "test", "lock"], + return), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + + SpecArg1 = string:join([Spec1,Spec2,Spec3],","), + {ok, GetOptResult1} = getopt:parse(GetOptSpec, ["--spec",SpecArg1]), + State1 = rebar_state:command_parsed_args(State, GetOptResult1), + Tests1 = rebar_prv_common_test:prepare_tests(State1), + {ok, NewState1} = rebar_prv_common_test:compile(State1, Tests1), + {ok, T1} = Tests1, + Opts1= rebar_prv_common_test:translate_paths(NewState1, T1), + + %% check that extra src dir is added + ExtraDir = filename:join([AppDir, "_build", "test", "extras"]), + [ExtraDir,"suites","test"] = + rebar_dir:extra_src_dirs(rebar_state:opts(NewState1)), + + %% check that path is translated + ExpectedSpec1 = filename:join([AppDir, "_build", "test", + "extras", "root.spec"]), + ExpectedSpec2 = filename:join([AppDir, "_build", "test", + "extras", "root1.spec"]), + ExpectedSpec3 = filename:join([AppDir, "_build", "test", + "extras", "root2.spec"]), + [ExpectedSpec1,ExpectedSpec2,ExpectedSpec3] = + lists:sort(proplists:get_value(spec, Opts1)), + + %% check that test specs are copied + [ExpectedSpec1,ExpectedSpec2,ExpectedSpec3] = + lists:sort(filelib:wildcard(filename:join([AppDir, "_build", "test", + "extras", "*.spec"]))), + + %% Same test again, using relative path + {ok,Cwd} = file:get_cwd(), + ok = file:set_cwd(AppDir), + ok = rebar_file_utils:rm_rf("_build"), + + SpecArg2 = "root.spec,root1.spec,root2.spec", + {ok, GetOptResult2} = getopt:parse(GetOptSpec, ["--spec",SpecArg2]), + State2 = rebar_state:command_parsed_args(State, GetOptResult2), + Tests2 = rebar_prv_common_test:prepare_tests(State2), + {ok, NewState2} = rebar_prv_common_test:compile(State2, Tests2), + {ok, T2} = Tests2, + Opts2= rebar_prv_common_test:translate_paths(NewState2, T2), + + %% check that extra src dir is added + [ExtraDir,"suites","test"] = + rebar_dir:extra_src_dirs(rebar_state:opts(NewState2)), + + %% check that path is translated + [ExpectedSpec1,ExpectedSpec2,ExpectedSpec3] = + lists:sort(proplists:get_value(spec, Opts2)), + + %% check that test specs are copied + [ExpectedSpec1,ExpectedSpec2,ExpectedSpec3] = + lists:sort(filelib:wildcard(filename:join([AppDir, "_build", "test", + "extras", "root*.spec"]))), + + ok = file:set_cwd(Cwd), + + ok. + cmd_vs_cfg_opts(Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_cmd_vs_cfg_opts_"), -- cgit v1.1 From d6a34f9d68b962205170c78dd1ef7b97f8475180 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Thu, 22 Dec 2016 00:48:49 -0800 Subject: don't filter eunit test modules based on file extension stops the eunit provider from filtering out test modules based on the file extension. previously, it was hardcoded to expect all test files ended in `.erl`. this change allows for endings like `.lfe` and `.beam` --- src/rebar_prv_eunit.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index a9db12e..f65f700 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -198,8 +198,8 @@ dedupe_tests({AppMods, TestMods}) -> %% for each modules in TestMods create a test if there is not a module %% in AppMods that will trigger it F = fun(Mod) -> - M = filename:basename(Mod, ".erl"), - MatchesTest = fun(Dir) -> filename:basename(Dir, ".erl") ++ "_tests" == M end, + M = filename:rootname(filename:basename(Mod)), + MatchesTest = fun(Dir) -> filename:rootname(filename:basename(Dir)) ++ "_tests" == M end, case lists:any(MatchesTest, AppMods) of false -> {true, {module, list_to_atom(M)}}; true -> false -- cgit v1.1 From 998c6756b73ec2c227ed6f7f2e66eb059d1027fc Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 22 Dec 2016 20:25:52 +0100 Subject: Make sure ct_testspec is loaded ... before calling erlang:function_exported(ct_testspec,get_tests,1). --- src/rebar_prv_common_test.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 4a9ba25..30596da 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -554,6 +554,7 @@ get_dirs_from_specs(Specs) -> end. get_tests_from_specs(Specs) -> + _ = ct_testspec:module_info(), % make sure ct_testspec is loaded case erlang:function_exported(ct_testspec,get_tests,1) of true -> ct_testspec:get_tests(Specs); -- cgit v1.1 From 146f2732b96b5db6476e3b86b13e7a3d7b1b2dc5 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 22 Dec 2016 22:14:07 +0100 Subject: Handle errors from ct_testspec --- src/rebar_prv_common_test.erl | 8 +++++--- test/rebar_ct_SUITE.erl | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 30596da..2ac8fc7 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -100,7 +100,9 @@ format_error({badconfig, Msg}) -> io_lib:format(Msg, []); format_error({multiple_errors, Errors}) -> io_lib:format(lists:concat(["Error running tests:"] ++ - lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []). + lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []); +format_error({error_reading_testspec, Reason}) -> + io_lib:format("Error reading testspec: ~p", [Reason]). %% =================================================================== %% Internal functions @@ -549,8 +551,8 @@ get_dirs_from_specs(Specs) -> RunList = lists:append([R || {_,R,_} <- NodeRunSkipList]), DirList = [element(1,R) || R <- RunList], {ok,{SpecList,DirList}}; - Error -> - Error + {error,Reason} -> + {error,{?MODULE,{error_reading_testspec,Reason}}} end. get_tests_from_specs(Specs) -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 91324d2..06dc76e 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -54,6 +54,7 @@ misspecified_ct_first_files/1, testspec/1, testspec_at_root/1, + testspec_parse_error/1, cmd_vs_cfg_opts/1]). -include_lib("eunit/include/eunit.hrl"). @@ -73,6 +74,7 @@ all() -> [{group, basic_app}, misspecified_ct_first_files, testspec, testspec_at_root, + testspec_parse_error, cmd_vs_cfg_opts]. groups() -> [{basic_app, [], [basic_app_default_dirs, @@ -1452,6 +1454,51 @@ testspec_at_root(Config) -> ok. +testspec_parse_error(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_testspec_error"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_testspec_error"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + Spec1 = filename:join([AppDir, "test", "nonexisting.spec"]), + Spec2 = filename:join([AppDir, "test", "some.spec"]), + ok = filelib:ensure_dir(Spec2), + ok = file:write_file(Spec2, ".\n"), + + {ok, State} = rebar_test_utils:run_and_check(C, + [], + ["as", "test", "lock"], + return), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + + %% Non existing testspec + {ok, GetOptResult1} = getopt:parse(GetOptSpec, ["--spec",Spec1]), + State1 = rebar_state:command_parsed_args(State, GetOptResult1), + Tests1 = rebar_prv_common_test:prepare_tests(State1), + {error, + {rebar_prv_common_test, + {error_reading_testspec, + {Spec1,"no such file or directory"}}}} = + rebar_prv_common_test:compile(State1, Tests1), + + %% Syntax error + {ok, GetOptResult2} = getopt:parse(GetOptSpec, ["--spec",Spec2]), + State2 = rebar_state:command_parsed_args(State, GetOptResult2), + Tests2 = rebar_prv_common_test:prepare_tests(State2), + {error, + {rebar_prv_common_test, + {error_reading_testspec, + {Spec2,"1: syntax error before: '.'"}}}} = + rebar_prv_common_test:compile(State2, Tests2), + + ok. + cmd_vs_cfg_opts(Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_cmd_vs_cfg_opts_"), -- cgit v1.1 From 5b3526518689eef8765a9303d43408864200b5d8 Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Fri, 23 Dec 2016 10:47:16 -0800 Subject: eunit: remove application modules from the modules eligible to test application modules will be added to the eunit test set automatically, no need to consider them for inclusion in the test set separately --- src/rebar_prv_eunit.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index f65f700..7d44137 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -195,17 +195,18 @@ gather_src([Dir|Rest], Regex, Srcs) -> gather_src(Rest, Regex, Srcs ++ rebar_utils:find_files(Dir, Regex, true)). dedupe_tests({AppMods, TestMods}) -> + UniqueTestMods = lists:usort(TestMods) -- AppMods, %% for each modules in TestMods create a test if there is not a module %% in AppMods that will trigger it - F = fun(Mod) -> - M = filename:rootname(filename:basename(Mod)), - MatchesTest = fun(Dir) -> filename:rootname(filename:basename(Dir)) ++ "_tests" == M end, + F = fun(TestMod) -> + M = filename:rootname(filename:basename(TestMod)), + MatchesTest = fun(AppMod) -> filename:rootname(filename:basename(AppMod)) ++ "_tests" == M end, case lists:any(MatchesTest, AppMods) of false -> {true, {module, list_to_atom(M)}}; true -> false end end, - lists:usort(rebar_utils:filtermap(F, TestMods)). + rebar_utils:filtermap(F, UniqueTestMods). inject_eunit_state(State, {ok, Tests}) -> Apps = rebar_state:project_apps(State), -- cgit v1.1 From c21a5f7ab2309bedfd430e71ece576cbfaa93114 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 23 Dec 2016 15:27:08 -0500 Subject: Bumping to 3.3.4 --- bootstrap | 2 +- src/rebar.app.src | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index 5359660..8a0ebed 100755 --- a/bootstrap +++ b/bootstrap @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.3.3", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.3.4", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence diff --git a/src/rebar.app.src b/src/rebar.app.src index 74efe97..83c8ecd 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "git"}, + {vsn, "3.3.4"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From 878cc18f800665b3aa74d9b9905a8a246a05edea Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Fri, 23 Dec 2016 15:33:22 -0500 Subject: Back to git-based versions --- src/rebar.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rebar.app.src b/src/rebar.app.src index 83c8ecd..74efe97 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.3.4"}, + {vsn, "git"}, {modules, []}, {registered, []}, {applications, [kernel, -- cgit v1.1 From fc92973024abb67399feb7215c4ab4a4301d67a2 Mon Sep 17 00:00:00 2001 From: Luis Rascao Date: Tue, 27 Dec 2016 18:31:58 +0000 Subject: Bump relx to 3.22.1 --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index a48fa41..7fbffa6 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.3.0"}, - {relx, "3.22.0"}, + {relx, "3.22.1"}, {cf, "0.2.2"}, {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. diff --git a/rebar.lock b/rebar.lock index f9a48f9..76869a0 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,7 +7,7 @@ {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.22.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.22.1">>},0}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ @@ -19,6 +19,6 @@ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"FF7E2B5924B754A63BA1A46BA8C1901A2D6AE1418982E189CFED5937DACE18DA">>}, + {<<"relx">>, <<"B79C220A0234C4360EE7B7C2FAAA0A07085DC6266712C12C03A073164D795081">>}, {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} ]. -- cgit v1.1 From 27cf4bfea0f7c0c58ae03f71b8db02648dd1ae04 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sat, 31 Dec 2016 16:40:54 -0800 Subject: add option to pass args to user_drv for custom shells --- src/rebar_prv_shell.erl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 72efcf1..c1bf735 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -75,7 +75,10 @@ init(State) -> "A list of apps to boot before starting the " "shell. (E.g. --apps app1,app2,app3) Defaults " "to rebar.config {shell, [{apps, Apps}]} or " - "relx apps if not specified."}]} + "relx apps if not specified."}, + {user_drv_args, undefined, "user_drv_args", string, + "Arguments passed to user_drv start function for " + "creating custom shells."}]} ]) ), {ok, State1}. @@ -99,7 +102,9 @@ format_error(Reason) -> shell(State) -> setup_name(State), setup_paths(State), - setup_shell(), + ShellArgs = debug_get_value(shell_args, rebar_state:get(State, shell, []), undefined, + "Found user_drv args from command line option."), + setup_shell(ShellArgs), maybe_run_script(State), %% apps must be started after the change in shell because otherwise %% their application masters never gets the new group leader (held in @@ -117,13 +122,13 @@ shell(State) -> info() -> "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n". -setup_shell() -> +setup_shell(ShellArgs) -> OldUser = kill_old_user(), %% Test for support here NewUser = try erlang:open_port({spawn,"tty_sl -c -e"}, []) of Port when is_port(Port) -> true = port_close(Port), - setup_new_shell() + setup_new_shell(ShellArgs) catch error:_ -> setup_old_shell() @@ -153,11 +158,16 @@ wait_for_port_death(N, P) -> wait_for_port_death(N-10, P) end. -setup_new_shell() -> +setup_new_shell(ShellArgs) -> %% terminate the current user supervision structure, if any _ = supervisor:terminate_child(kernel_sup, user), %% start a new shell (this also starts a new user under the correct group) - _ = user_drv:start(), + case ShellArgs of + undefined -> + _ = user_drv:start(); + _ -> + _ = user_drv:start(ShellArgs) + end, %% wait until user_drv and user have been registered (max 3 seconds) ok = wait_until_user_started(3000), whereis(user). -- cgit v1.1 From a259d5279215ac03511731a9ecd303a72907b1bd Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 6 Jan 2017 11:38:56 -0800 Subject: upgrade relx to fixed hex package 3.22.2 --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 7fbffa6..ca80dce 100644 --- a/rebar.config +++ b/rebar.config @@ -7,7 +7,7 @@ {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.3.0"}, - {relx, "3.22.1"}, + {relx, "3.22.2"}, {cf, "0.2.2"}, {cth_readable, "1.2.3"}, {eunit_formatters, "0.3.1"}]}. diff --git a/rebar.lock b/rebar.lock index 76869a0..9a52350 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,7 +7,7 @@ {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.22.1">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.22.2">>},0}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. [ {pkg_hash,[ @@ -19,6 +19,6 @@ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"B79C220A0234C4360EE7B7C2FAAA0A07085DC6266712C12C03A073164D795081">>}, + {<<"relx">>, <<"AEE2EF6E9AC6D21D6661133B7A0BE6E81424DE9CDCA0012FC008BC677297C469">>}, {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} ]. -- cgit v1.1 From c2f0d1ca0a2832f8475365e0c8cef752af9850cb Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Tue, 10 Jan 2017 17:13:32 -0800 Subject: upgrade erlware_commons to 1.0.0 for OTP20 support --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index ca80dce..1d853c4 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,7 @@ %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -{deps, [{erlware_commons, "0.22.0"}, +{deps, [{erlware_commons, "1.0.0"}, {ssl_verify_fun, "1.1.1"}, {certifi, "0.4.0"}, {providers, "1.6.0"}, diff --git a/rebar.lock b/rebar.lock index 9a52350..abcee59 100644 --- a/rebar.lock +++ b/rebar.lock @@ -3,7 +3,7 @@ {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.2.2">>},0}, {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.22.0">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.0.0">>},0}, {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, @@ -15,7 +15,7 @@ {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, {<<"cf">>, <<"7F2913FFF90ABCABD0F489896CFEB0B0674F6C8DF6C10B17A83175448029896C">>}, {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, - {<<"erlware_commons">>, <<"051BED79A34E66678C1CBEEBC7B66360C827D081A0C5E2528878011E31DDCDCA">>}, + {<<"erlware_commons">>, <<"087467DE5833C0BB5B3CCDD387F9E9C1FB816A75B7A709629BF24B5ED3246C51">>}, {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, -- cgit v1.1 From 05efc56b62108e20ffb359b90f74f75bd614bdee Mon Sep 17 00:00:00 2001 From: Andrew McRobb Date: Wed, 18 Jan 2017 12:01:03 -0700 Subject: [FEATURE] Add .editorConfig Settings. [skip ci] https://github.com/erlang/rebar3/issues/1443 --- .editorconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7e2f83d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig file: http://EditorConfig.org + +# Top-most EditorConfig file. +root = true + +# Unix-style, newlines, indent style of 4 spaces, with a newline ending every file. +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 -- cgit v1.1 From 285373b9d3475c40f1ac30323f488f5e053ee13a Mon Sep 17 00:00:00 2001 From: Andrew McRobb Date: Fri, 20 Jan 2017 00:16:45 -0700 Subject: [BUGFIX] Fixed missing hypens, and improper wording. [skip ci] --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f44c7bd..6b95f91 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ others via the plugin ecosystem: | Tarballs | Releases can be packaged into tarballs ready to be deployed. | | Templates | Configurable templates ship out of the box (try `rebar3 new` for a list or `rebar3 new help