diff options
55 files changed, 872 insertions, 195 deletions
@@ -1,12 +1,11 @@ -*.beam -rebar +/ebin/*.beam +/rebar *~ *.orig .*.swp -rt.work -.test -dialyzer_warnings -rebar.cmd -.eunit -deps -.rebar/* +/rt.work +/dialyzer_warnings +/rebar.cmd +/.eunit +/deps +/.rebar diff --git a/.travis.yml b/.travis.yml index a7eedb4..602266b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,5 @@ otp_release: - R15B - R14B04 - R14B03 + - 17.0 script: "make travis" diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index bdfb3d7..093e771 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,33 +1,64 @@ +# Rebar 2.3.1 + +## PR's Merged + +* rebar/242: [Extra commits for #129](https://github.com/rebar/rebar/pull/242) +* rebar/244: [Document skip_apps=, apps=, and require_*_vsn](https://github.com/rebar/rebar/pull/244) +* rebar/251: [Make sure that eunit/qc_compile_opts works](https://github.com/rebar/rebar/pull/251) +* rebar/255: [rebar.app: remove superfluous quoting](https://github.com/rebar/rebar/pull/255) +* rebar/274: [Use lowercase for Windows drive name to resolve issue #250](https://github.com/rebar/rebar/pull/274) + +# Rebar 2.3.0 + +## PR's Merged + +* rebar/98: [Repetition of environment variable definitions in child processes (ports)](https://github.com/rebar/rebar/pull/98) +* rebar/115: [Incorrect REMSH args when sname is used.](https://github.com/rebar/rebar/pull/115) +* rebar/129: [Speed up the compilation process v5](https://github.com/rebar/rebar/pull/129) +* rebar/139: [Allow specification of module dependencies for appups](https://github.com/rebar/rebar/pull/139) +* rebar/175: [CWD plugins regression](https://github.com/rebar/rebar/pull/175) +* rebar/188: [Xref extra path](https://github.com/rebar/rebar/pull/188) +* rebar/208: [Fix typo in rebar_erlydtl_compiler](https://github.com/rebar/rebar/pull/208) +* rebar/219: [Added R16B01 and R16B02 to travis config.](https://github.com/rebar/rebar/pull/219) +* rebar/221: [Adapt erlydtl compiler plugin to latest version of erlydtl](https://github.com/rebar/rebar/pull/221) +* rebar/223: [Add random_suite_order option to eunit command](https://github.com/rebar/rebar/pull/223) +* rebar/224: [allow suites or tests as options for eunit and ct](https://github.com/rebar/rebar/pull/224) +* rebar/230: [eunit: fix dialyzer warnings introduced in 03da5e0b](https://github.com/rebar/rebar/pull/230) +* rebar/232: [Document support for abbreviated commands](https://github.com/rebar/rebar/pull/232) +* rebar/233: [docs: fix #228](https://github.com/rebar/rebar/pull/233) +* rebar/234: [Fix #220 (Reported-by: Joseph Norton)](https://github.com/rebar/rebar/pull/234) +* rebar/237: [Add partial support for Erlang/OTP 17](https://github.com/rebar/rebar/pull/237) +* rebar/252: [file_utils: properly report errors (fix #95)](https://github.com/rebar/rebar/pull/252) +* rebar/254: [Fix 'rebar generate' regression (#253)](https://github.com/rebar/rebar/pull/254) +* rebar/265: [Fix 'rebar help clean' function_clause error](https://github.com/rebar/rebar/pull/265) +* rebar/268: [Fix #267 (code path regression)](https://github.com/rebar/rebar/pull/268) +* rebar/269: [Update THANKS](https://github.com/rebar/rebar/pull/269) + # Rebar 2.2.0 ## PR's Merged -* rebar/137: [Filter documented behaviour callbacks](https://github.com/basho/rebar/pull/137) -* rebar/142: [Fix rebar_file_utils module on Windows with MSYS](https://github.com/basho/rebar/pull/142) -* rebar/152: [Allow users to configure skip_deps for specific commands](https://github.com/basho/rebar/pull/152) -* rebar/154: [Stop applications nicely before killing extra processes](https://github.com/basho/rebar/pull/154) -* rebar/155: [Fix rebar_utils:expand_env_variable/3](https://github.com/basho/rebar/pull/155) -* rebar/157: [add native Windows compiler support](https://github.com/basho/rebar/pull/157) -* rebar/172: [Allow reltool target_dir to be constructed on the fly](https://github.com/basho/rebar/pull/172) -* rebar/173: [rebar should expand VCS version in the top directory, if possible](https://github.com/basho/rebar/pull/173) -* rebar/174: [Fixed handle_call response in simplesrv.erl](https://github.com/basho/rebar/pull/174) -* rebar/177: [Cache vsn information during the run to avoid extra unnecessary shell calls](https://github.com/basho/rebar/pull/177) -* rebar/179: [Add ebin to the path before compiling erlydtl templates](https://github.com/basho/rebar/pull/179) -* rebar/183: [Fix compiling DTL templates with latest erlydtl](https://github.com/basho/rebar/pull/183) -* rebar/184: [Fix for destruction of config app vars on reset](https://github.com/basho/rebar/pull/184) -* rebar/185: [simple enhance and simple bugfix](https://github.com/basho/rebar/pull/185) -* rebar/187: [fix for cp_r_win32 where copying a directory to a non-existant directory would crash](https://github.com/basho/rebar/pull/187) -* rebar/189: [Fix typos in generated cmd script in bootstrap](https://github.com/basho/rebar/pull/189) -* rebar/190: [Windows xcopy dir to non-existant dir (re-request)](https://github.com/basho/rebar/pull/190) -* rebar/191: [Fix typos in rebar_templater](https://github.com/basho/rebar/pull/191) -* rebar/196: [Escape '|' in the Windows runner usage string](https://github.com/basho/rebar/pull/196) -* rebar/198: [New feature to rebar_xref to allow execution of custom queries.](https://github.com/basho/rebar/pull/198) -* rebar/199: [Added new feature to rebar xref to allow execution of custom queries.](https://github.com/basho/rebar/pull/199) -* rebar/200: [Enable runner to pass more than one argument to start](https://github.com/basho/rebar/pull/200) -* rebar/201: [include simplemodule.app.src in simplemod template](https://github.com/basho/rebar/pull/201) -* rebar/205: [Ports in languages other than C](https://github.com/basho/rebar/issues/205) -* rebar/210: [use file:script if a .config.script file present](https://github.com/basho/rebar/pull/210) -* rebar/212: [ Modified simplenode.runner to start from alternative directory](https://github.com/basho/rebar/pull/212) -* rebar/214: [Foreground running doesn't allow console attaching](https://github.com/basho/rebar/issues/214) -* rebar/215: [Add support for http proxy_friendly_github_urls](https://github.com/basho/rebar/pull/215) -* rebar/388: [Less than useful rebar error message when error in .hrl files](https://github.com/basho/rebar/issues/388) +* rebar/152: [Fix erl_opts use](https://github.com/rebar/rebar/pull/152) +* rebar/154: [Fix update-deps with certain forms of the {tag, ...} type](https://github.com/rebar/rebar/pull/154) +* rebar/155: [Fixes for #137 and #142](https://github.com/rebar/rebar/pull/155) +* rebar/157: [Don't over-aggressively clean the code path in the presence of lib_dir directives](https://github.com/rebar/rebar/pull/157) +* rebar/172: [Add missing dep examples and fix existing ones](https://github.com/rebar/rebar/pull/172) +* rebar/173: [Fix false reporting of (plain) vsn strings](https://github.com/rebar/rebar/pull/173) +* rebar/174: [rebar_core: fix Dialyzer warning introduced in aa46d85 (#157)](https://github.com/rebar/rebar/pull/174) +* rebar/177: [Delete unused inttest/retest binary](https://github.com/rebar/rebar/pull/177) +* rebar/179: [Make list of commands (for unabbreviation) easier to maintain](https://github.com/rebar/rebar/pull/179) +* rebar/183: [generate-upgrade can now take target_dir argument](https://github.com/rebar/rebar/pull/183) +* rebar/184: [Fix log levels](https://github.com/rebar/rebar/pull/184) +* rebar/185: [Switch retest dep to upstream (dizzyd/retest.git)](https://github.com/rebar/rebar/pull/185) +* rebar/189: [inttest/rgen1: increase retest timeout (30s -> 60s)](https://github.com/rebar/rebar/pull/189) +* rebar/190: [inttest/rgen_1: double the timeout a second time](https://github.com/rebar/rebar/pull/190) +* rebar/191: [Fix #187 (rename getopt and mustache)](https://github.com/rebar/rebar/pull/191) +* rebar/196: [Print a more appropriate message on 'rebar info'](https://github.com/rebar/rebar/pull/196) +* rebar/198: [Clean up rebar.config.script](https://github.com/rebar/rebar/pull/198) +* rebar/199: [rebar_dia_compiler: fix Dialyzer warnings](https://github.com/rebar/rebar/pull/199) +* rebar/200: [bootstrap: avoid trying to run 'debug' command](https://github.com/rebar/rebar/pull/200) +* rebar/201: [Added a library template.](https://github.com/rebar/rebar/pull/201) +* rebar/210: [Fix #205 (erlydtl:compile/3 returns warnings)](https://github.com/rebar/rebar/pull/210) +* rebar/212: [Fix basho/rebar#388](https://github.com/rebar/rebar/pull/212) +* rebar/214: [Document compile_only=true](https://github.com/rebar/rebar/pull/214) +* rebar/215: [Remove experimental flags](https://github.com/rebar/rebar/pull/215) @@ -121,3 +121,7 @@ Sylvain Benner Oliver Ferrigni Dave Thomas Evgeniy Khramtsov +YeJun Su +Yuki Ito +alisdair sullivan +Alexander Verbitsky @@ -28,10 +28,20 @@ main(Args) -> %% Extract the system info of the version of OTP we use to compile rebar OtpInfo = string:strip(erlang:system_info(otp_release), both, $\n), + %% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17. + %% At the same time, their counterparts dict() and digraph() are to be deprecated + %% in Erlang 18. namespaced_types option is used to select proper type name + %% depending of the OTP version used. + NamespacedTypes = case is_otp(OtpInfo, "^[0-9]+") of + true -> {d, namespaced_types}; + false -> undefined + end, + %% Compile all src/*.erl to ebin case make:files(filelib:wildcard("src/*.erl"), [{outdir, "ebin"}, {i, "include"}, DebugFlag, + NamespacedTypes, {d, 'BUILD_TIME', Built}, {d, 'VCS_INFO', VcsInfo}, {d, 'OTP_INFO', OtpInfo}]) of @@ -79,6 +89,12 @@ main(Args) -> "Place this script anywhere in your path\n" "and you can use rebar to build OTP-compliant apps.\n"). +is_otp(OtpInfo, Regex) -> + case re:run(OtpInfo, Regex, [{capture, none}]) of + match -> true; + nomatch -> false + end. + rm(Path) -> NativePath = filename:nativename(Path), Cmd = case os:type() of diff --git a/ebin/rebar.app b/ebin/rebar.app index 6b4702a..29ad8cf 100644 --- a/ebin/rebar.app +++ b/ebin/rebar.app @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "2.2.0"}, + {vsn, "2.3.1"}, {modules, [ rebar, rebar_abnfc_compiler, rebar_app_utils, @@ -93,7 +93,7 @@ ]}, {recursive_cmds, [ 'check-deps', - 'compile', + compile, 'delete-deps', 'get-deps', 'list-deps', diff --git a/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl b/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl new file mode 100644 index 0000000..d884bcc --- /dev/null +++ b/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl @@ -0,0 +1,19 @@ +-module(code_path_no_recurse_rt). +-export([files/0, + run/1]). + +files() -> + [ + {copy, "../../rebar", "rebar"}, + {copy, "rebar.config", "rebar.config"}, + {copy, "src", "src"}, + {copy, "test", "test"}, + {copy, "deps", "deps"} + ]. + +run(_Dir) -> + retest:log(info, "Compile project~n"), + {ok, _} = retest:sh("./rebar -v compile"), + retest:log(info, "Run eunit with referenced deps on the code path~n"), + {ok, _} = retest:sh("./rebar -v eunit"), + ok. diff --git a/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src new file mode 100644 index 0000000..7f7b3f9 --- /dev/null +++ b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src @@ -0,0 +1,12 @@ +{application, bazdep, + [ + {description, ""}, + {vsn, "1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, {bazdep, []}}, + {env, []} + ]}. diff --git a/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl new file mode 100644 index 0000000..aef4cf3 --- /dev/null +++ b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl @@ -0,0 +1,6 @@ +-module(bazdep). + +-export([bazdep/0]). + +bazdep() -> + bazdep. diff --git a/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl b/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl new file mode 100644 index 0000000..b5190f6 --- /dev/null +++ b/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl @@ -0,0 +1,5 @@ +-module(bazdep_tests). +-include_lib("eunit/include/eunit.hrl"). + +bazdep_test() -> + ?assert(bazdep:bazdep() =:= bazdep). diff --git a/inttest/code_path_no_recurse/deps/foodep/rebar.config b/inttest/code_path_no_recurse/deps/foodep/rebar.config new file mode 100644 index 0000000..cdaf168 --- /dev/null +++ b/inttest/code_path_no_recurse/deps/foodep/rebar.config @@ -0,0 +1 @@ +{deps, [{bazdep, "1"}]}. diff --git a/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src b/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src new file mode 100644 index 0000000..c0642fb --- /dev/null +++ b/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src @@ -0,0 +1,12 @@ +{application, foodep, + [ + {description, ""}, + {vsn, "1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, {foodep, []}}, + {env, []} + ]}. diff --git a/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl b/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl new file mode 100644 index 0000000..3d43d0e --- /dev/null +++ b/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl @@ -0,0 +1,6 @@ +-module(foodep). + +-export([foodep/0]). + +foodep() -> + bazdep:bazdep() =:= bazdep. diff --git a/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl b/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl new file mode 100644 index 0000000..66d7b8b --- /dev/null +++ b/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl @@ -0,0 +1,5 @@ +-module(foodep_tests). +-include_lib("eunit/include/eunit.hrl"). + +foodep_test() -> + ?assert(foodep:foodep()). diff --git a/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src new file mode 100644 index 0000000..d0bc233 --- /dev/null +++ b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src @@ -0,0 +1,12 @@ +{application, unuseddep, + [ + {description, ""}, + {vsn, "1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, {unuseddep, []}}, + {env, []} + ]}. diff --git a/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl new file mode 100644 index 0000000..a990345 --- /dev/null +++ b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl @@ -0,0 +1,6 @@ +-module(unuseddep). + +-export([unuseddep/0]). + +unuseddep() -> + unuseddep. diff --git a/inttest/code_path_no_recurse/rebar.config b/inttest/code_path_no_recurse/rebar.config new file mode 100644 index 0000000..4b358de --- /dev/null +++ b/inttest/code_path_no_recurse/rebar.config @@ -0,0 +1 @@ +{deps, [{foodep, "1"}]}. diff --git a/inttest/code_path_no_recurse/src/codepath.app.src b/inttest/code_path_no_recurse/src/codepath.app.src new file mode 100644 index 0000000..3aa200f --- /dev/null +++ b/inttest/code_path_no_recurse/src/codepath.app.src @@ -0,0 +1,12 @@ +{application, codepath, + [ + {description, ""}, + {vsn, "1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {mod, {codepath, []}}, + {env, []} + ]}. diff --git a/inttest/code_path_no_recurse/src/codepath.erl b/inttest/code_path_no_recurse/src/codepath.erl new file mode 100644 index 0000000..df4e6b0 --- /dev/null +++ b/inttest/code_path_no_recurse/src/codepath.erl @@ -0,0 +1,6 @@ +-module(codepath). + +-export([codepath/0]). + +codepath() -> + foodep:foodep(). diff --git a/inttest/code_path_no_recurse/test/codepath_tests.erl b/inttest/code_path_no_recurse/test/codepath_tests.erl new file mode 100644 index 0000000..01a1d2a --- /dev/null +++ b/inttest/code_path_no_recurse/test/codepath_tests.erl @@ -0,0 +1,12 @@ +-module(codepath_tests). +-include_lib("eunit/include/eunit.hrl"). + +codepath_test() -> + ?assertEqual({module, codepath}, code:ensure_loaded(codepath)), + ?assertEqual({module, foodep}, code:ensure_loaded(foodep)), + ?assertEqual({module, bazdep}, code:ensure_loaded(bazdep)), + ?assert(codepath:codepath()). + +unuseddep_test() -> + ?assertEqual(non_existing, code:which(unuseddep)), + ?assertEqual({error, nofile}, code:ensure_loaded(unuseddep)). diff --git a/inttest/erlc/asn1/SIMPLE-ASN.asn1 b/inttest/erlc/asn1/SIMPLE-ASN.asn1 new file mode 100644 index 0000000..62f0860 --- /dev/null +++ b/inttest/erlc/asn1/SIMPLE-ASN.asn1 @@ -0,0 +1,7 @@ +SIMPLE-ASN DEFINITIONS ::= BEGIN + + SimpleMessage ::= SEQUENCE { + id INTEGER + } + +END diff --git a/inttest/erlc/erlc_rt.erl b/inttest/erlc/erlc_rt.erl new file mode 100644 index 0000000..354ad29 --- /dev/null +++ b/inttest/erlc/erlc_rt.erl @@ -0,0 +1,98 @@ +-module(erlc_rt). +-export([files/0, + run/1]). + +-include_lib("eunit/include/eunit.hrl"). + +-define(MODULES, + [first_xrl, + first_yrl, + foo, + foo_app, + foo_sup, + foo_test_worker, + foo_worker, + 'SIMPLE-ASN']). + +-define(BEAM_FILES, + ["first_xrl.beam", + "first_yrl.beam", + "foo.beam", + "foo_app.beam", + "foo_sup.beam", + "foo_test_worker.beam", + "foo_worker.beam", + "SIMPLE-ASN.beam"]). + +files() -> + [ + {copy, "../../rebar", "rebar"}, + {copy, "rebar.config", "rebar.config"}, + {copy, "rebar-no_debug_info.config", "rebar-no_debug_info.config"}, + {copy, "include", "include"}, + {copy, "extra-include", "extra-include"}, + {copy, "src", "src"}, + {copy, "extra-src", "extra-src"}, + {copy, "mibs", "mibs"}, + {copy, "asn1", "asn1"}, + {create, "ebin/foo.app", app(foo, ?MODULES)} + ]. + +run(_Dir) -> + ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), + ok = check_beams(true), + ok = check_debug_info(true), + MibResult = filename:join(["priv", "mibs", "SIMPLE-MIB.bin"]), + ?assertMatch(true, filelib:is_regular(MibResult)), + ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])), + ok = check_beams(false), + ?assertMatch(false, filelib:is_regular(MibResult)), + ?assertMatch( + {ok, _}, + retest_sh:run("./rebar -C rebar-no_debug_info.config compile", [])), + ok = check_beams(true), + ok = check_debug_info(false), + ?assertMatch(true, filelib:is_regular(MibResult)), + ok. + +check_beams(Exist) -> + check_files(Exist, fun filelib:is_regular/1). + +check_debug_info(HasDebugInfo) -> + check_files(HasDebugInfo, fun has_debug_info/1). + +check_files(Expected, Check) -> + lists:foreach( + fun(F) -> + File = filename:join("ebin", F), + ?assertEqual(Expected, Check(File)) + end, + ?BEAM_FILES). + +%% NOTE: Copied from dialyzer_utils:get_abstract_code_from_beam/1 and +%% modified for local use. We could have called the function directly, +%% but dialyzer_utils is not an official API to rely on. +has_debug_info(File) -> + case beam_lib:chunks(File, [abstract_code]) of + {ok, {_Mod, List}} -> + case lists:keyfind(abstract_code, 1, List) of + {abstract_code, {raw_abstract_v1, _Abstr}} -> + true; + _ -> + false + end; + _ -> + false + end. + +%% +%% Generate the contents of a simple .app file +%% +app(Name, Modules) -> + App = {application, Name, + [{description, atom_to_list(Name)}, + {vsn, "1"}, + {modules, Modules}, + {registered, []}, + {applications, [kernel, stdlib]}]}, + io_lib:format("~p.\n", [App]). diff --git a/inttest/erlc/extra-include/foo_extra.hrl b/inttest/erlc/extra-include/foo_extra.hrl new file mode 100644 index 0000000..228affa --- /dev/null +++ b/inttest/erlc/extra-include/foo_extra.hrl @@ -0,0 +1 @@ +-define(FOO_EXTRA, foo_extra). diff --git a/inttest/erlc/extra-src/foo_sup.erl b/inttest/erlc/extra-src/foo_sup.erl new file mode 100644 index 0000000..a0f06b6 --- /dev/null +++ b/inttest/erlc/extra-src/foo_sup.erl @@ -0,0 +1,13 @@ +-module(foo_sup). + +-behavior(supervisor). + +-export([start_link/0, + init/1]). + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init(_Args) -> + FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]}, + {ok,{{one_for_all,1,1}, [FooChild]}}. diff --git a/inttest/erlc/include/foo_core.hrl b/inttest/erlc/include/foo_core.hrl new file mode 100644 index 0000000..2363140 --- /dev/null +++ b/inttest/erlc/include/foo_core.hrl @@ -0,0 +1 @@ +-define(FOO_CORE, foo_core). diff --git a/inttest/erlc/mibs/SIMPLE-MIB.mib b/inttest/erlc/mibs/SIMPLE-MIB.mib new file mode 100644 index 0000000..ca8735a --- /dev/null +++ b/inttest/erlc/mibs/SIMPLE-MIB.mib @@ -0,0 +1,26 @@ +-- SIMPLE-MIB. +-- This is just a simple MIB used for testing! +-- + + +SIMPLE-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, enterprises + FROM SNMPv2-SMI; + +ericsson MODULE-IDENTITY + LAST-UPDATED + "201403060000Z" + ORGANIZATION + "rebar" + CONTACT-INFO + "rebar <rebar@example.com> + or + whoever is currently responsible for the SIMPLE + enterprise MIB tree branch (enterprises.999)." + DESCRIPTION + "This very small module is made available + for mib-compilation testing." + ::= { enterprises 999 } +END diff --git a/inttest/erlc/rebar-no_debug_info.config b/inttest/erlc/rebar-no_debug_info.config new file mode 100644 index 0000000..1a7113a --- /dev/null +++ b/inttest/erlc/rebar-no_debug_info.config @@ -0,0 +1,11 @@ +%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 ft=erlang et +{erl_first_files, ["first_xrl.erl", "first_yrl.erl"]}. + +{erl_opts, + [ + no_debug_info, + {i, "extra-include"}, + {src_dirs, ["src", "extra-src"]}, + {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} + ]}. diff --git a/inttest/erlc/rebar.config b/inttest/erlc/rebar.config new file mode 100644 index 0000000..6a1082a --- /dev/null +++ b/inttest/erlc/rebar.config @@ -0,0 +1,10 @@ +%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 ft=erlang et +{erl_first_files, ["first_xrl.erl", "first_yrl.erl"]}. + +{erl_opts, + [ + {i, "extra-include"}, + {src_dirs, ["src", "extra-src"]}, + {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} + ]}. diff --git a/inttest/erlc/src/behaviour/foo_worker.erl b/inttest/erlc/src/behaviour/foo_worker.erl new file mode 100644 index 0000000..307c69a --- /dev/null +++ b/inttest/erlc/src/behaviour/foo_worker.erl @@ -0,0 +1,14 @@ +-module(foo_worker). + +-ifdef(NO_CALLBACK_ATTRIBUTE). + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> [{status, 0}]; +behaviour_info(_) -> undefined. + +-else. + +-callback status() -> 'idle' | 'busy'. + +-endif. diff --git a/inttest/erlc/src/first_xrl.xrl b/inttest/erlc/src/first_xrl.xrl new file mode 100644 index 0000000..0de4c70 --- /dev/null +++ b/inttest/erlc/src/first_xrl.xrl @@ -0,0 +1,13 @@ +Definitions. + +D = [0-9] + +Rules. + +{D}+ : + {token,{integer,TokenLine,list_to_integer(TokenChars)}}. + +{D}+\.{D}+((E|e)(\+|\-)?{D}+)? : + {token,{float,TokenLine,list_to_float(TokenChars)}}. + +Erlang code. diff --git a/inttest/erlc/src/first_yrl.yrl b/inttest/erlc/src/first_yrl.yrl new file mode 100644 index 0000000..8ccdb0e --- /dev/null +++ b/inttest/erlc/src/first_yrl.yrl @@ -0,0 +1,9 @@ +Nonterminals list elements element. +Terminals atom '(' ')'. +Rootsymbol list. +list -> '(' ')'. +list -> '(' elements ')'. +elements -> element. +elements -> element elements. +element -> atom. +element -> list. diff --git a/inttest/erlc/src/foo.erl b/inttest/erlc/src/foo.erl new file mode 100644 index 0000000..33e6cfc --- /dev/null +++ b/inttest/erlc/src/foo.erl @@ -0,0 +1,35 @@ +-module(foo). + +-export([start_link/0, + start_link/1, + init/1, + terminate/2, + handle_info/2, + handle_call/3, + handle_cast/2, + code_change/3]). + +-behavior(gen_server). + +-include("foo_core.hrl"). +-include("foo_extra.hrl"). +-include_lib("kernel/include/file.hrl"). + +-record(state, {node :: node()}). + +start_link() -> start_link(undefined). + +start_link(Args) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []). + +init(_Args) -> {ok, #state{node=node()}}. + +terminate(_Reason, _Data) -> ok. + +handle_info(_Info, State) -> {noreply, State}. + +handle_cast(_Msg, State) -> {noreply, State}. + +handle_call(_Msg, _From, State) -> {reply, ok, State}. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/inttest/erlc/src/foo_app.erl b/inttest/erlc/src/foo_app.erl new file mode 100644 index 0000000..a3c7a96 --- /dev/null +++ b/inttest/erlc/src/foo_app.erl @@ -0,0 +1,10 @@ +-module(foo_app). + +-behaviour(application). + +-export([start/2, + stop/1]). + +start(_Type, _Args) -> foo_sup:start_link(). + +stop(_State) -> ok. diff --git a/inttest/erlc/src/foo_test_worker.erl b/inttest/erlc/src/foo_test_worker.erl new file mode 100644 index 0000000..96ae932 --- /dev/null +++ b/inttest/erlc/src/foo_test_worker.erl @@ -0,0 +1,34 @@ +-module(foo_test_worker). + +-behaviour(gen_server). +-behaviour(foo_worker). + +-export([start_link/0, + start_link/1, + init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3, + status/0]). + +-include_lib("kernel/include/inet.hrl"). + +start_link() -> start_link(undefined). + +start_link(Args) -> gen_server:start_link(?MODULE, Args, []). + +init([]) -> {ok, undefined}. + +handle_call(_Event, _From, State) -> {reply, ok, State}. + +handle_cast(_Event, State) -> {noreply, State}. + +handle_info(_Info, State) -> {noreply, State}. + +terminate(_Reason, _State) -> ok. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. + +status() -> busy. diff --git a/inttest/eunit/eunit_rt.erl b/inttest/eunit/eunit_rt.erl new file mode 100644 index 0000000..47f3331 --- /dev/null +++ b/inttest/eunit/eunit_rt.erl @@ -0,0 +1,48 @@ +-module(eunit_rt). +-export([files/0, run/1]). + +-include_lib("eunit/include/eunit.hrl"). + +files() -> + [{create, "ebin/foo.app", app(foo)}, + {copy, "../../rebar", "rebar"}, + {copy, "src", "src"}, + {copy, "eunit_src", "eunit_src"}, + {copy, + "rebar-eunit_compile_opts.config", + "rebar-eunit_compile_opts.config"}]. + +run(_Dir) -> + ifdef_test(), + eunit_compile_opts_test(), + ok. + +ifdef_test() -> + {ok, Output} = retest:sh("./rebar -v eunit"), + ?assert(check_output(Output, "foo_test")), + ?assertMatch({ok, _}, retest:sh("./rebar clean")). + +eunit_compile_opts_test() -> + {ok, Output} = + retest:sh("./rebar -v -C rebar-eunit_compile_opts.config eunit"), + ?assert(check_output(Output, "bar_test")), + ?assertMatch( + {ok, _}, + retest:sh("./rebar -C rebar-eunit_compile_opts.config clean")). + +check_output(Output, Target) -> + lists:any(fun(Line) -> + string:str(Line, Target) > 0 + end, Output). + +%% +%% Generate the contents of a simple .app file +%% +app(Name) -> + App = {application, Name, + [{description, atom_to_list(Name)}, + {vsn, "1"}, + {modules, []}, + {registered, []}, + {applications, [kernel, stdlib]}]}, + io_lib:format("~p.\n", [App]). diff --git a/inttest/eunit/eunit_src/bar.erl b/inttest/eunit/eunit_src/bar.erl new file mode 100644 index 0000000..6a80dac --- /dev/null +++ b/inttest/eunit/eunit_src/bar.erl @@ -0,0 +1,6 @@ +-module(bar). + +-include_lib("eunit/include/eunit.hrl"). + +bar_test() -> + ?assert(true). diff --git a/inttest/eunit/rebar-eunit_compile_opts.config b/inttest/eunit/rebar-eunit_compile_opts.config new file mode 100644 index 0000000..13b2d94 --- /dev/null +++ b/inttest/eunit/rebar-eunit_compile_opts.config @@ -0,0 +1 @@ +{eunit_compile_opts, [{src_dirs, ["eunit_src"]}]}. diff --git a/inttest/eunit/src/foo.erl b/inttest/eunit/src/foo.erl new file mode 100644 index 0000000..a4c91ba --- /dev/null +++ b/inttest/eunit/src/foo.erl @@ -0,0 +1,10 @@ +-module(foo). + +-ifdef(TEST). + +-include_lib("eunit/include/eunit.hrl"). + +foo_test() -> + ?assert(true). + +-endif. diff --git a/priv/templates/simplenode.install_upgrade.escript b/priv/templates/simplenode.install_upgrade.escript index 56cea19..0d4cbe3 100644..100755 --- a/priv/templates/simplenode.install_upgrade.escript +++ b/priv/templates/simplenode.install_upgrade.escript @@ -6,6 +6,14 @@ -define(TIMEOUT, 60000). -define(INFO(Fmt,Args), io:format(Fmt,Args)). +%% TODO: This script currently does NOT support slim releases. +%% Necessary steps to upgrade a slim release are as follows: +%% 1. unpack relup archive manually +%% 2. copy releases directory and necessary libraries +%% 3. using release_hander:set_unpacked/2 . +%% For more details, see https://github.com/rebar/rebar/pull/52 +%% and https://github.com/rebar/rebar/issues/202 + main([NodeName, Cookie, ReleasePackage]) -> TargetNode = start_distribution(NodeName, Cookie), {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release, diff --git a/priv/templates/simplenode.reltool.config b/priv/templates/simplenode.reltool.config index bac7270..eae8ab3 100644 --- a/priv/templates/simplenode.reltool.config +++ b/priv/templates/simplenode.reltool.config @@ -32,7 +32,7 @@ {overlay, [ {mkdir, "log/sasl"}, {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, - {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"}, + {copy, "files/nodetool", "releases/\{\{rel_vsn\}\}/nodetool"}, {copy, "{{nodeid}}/bin/start_clean.boot", "\{\{erts_vsn\}\}/bin/start_clean.boot"}, {copy, "files/{{nodeid}}", "bin/{{nodeid}}"}, diff --git a/priv/templates/simplenode.runner b/priv/templates/simplenode.runner index 032eb28..3e9e10f 100755 --- a/priv/templates/simplenode.runner +++ b/priv/templates/simplenode.runner @@ -100,7 +100,7 @@ case "$REMSH_NAME" in *) REMSH_NAME_PART="$REMSH_NAME" if [ "$REMSH_TYPE" = "-sname" ]; then - REMSH_HOSTNAME_PART= "$HOSTNAME" + REMSH_HOSTNAME_PART="$HOSTNAME" else # -name type, check if `hostname` is fqdn if [ "$MAYBE_FQDN_HOSTNAME" = "$HOSTNAME" ]; then @@ -130,11 +130,43 @@ cd $USE_DIR # Make sure log directory exists mkdir -p $USE_DIR/log -# Add ERTS bin dir to our path -ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin +RUNNER_SCRIPT_DATA= +if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/runner_script.data" ]; then + RUNNER_SCRIPT_DATA=`cat $RUNNER_BASE_DIR/releases/$APP_VSN/runner_script.data` +fi -# Setup command to control the node -NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" +if [ -z "$RUNNER_SCRIPT_DATA" ]; then + ROOTDIR=$RUNNER_BASE_DIR + ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin + if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/nodetool" ]; then + NODETOOL="$ERTS_PATH/escript $RUNNER_BASE_DIR/releases/$APP_VSN/nodetool $NAME_ARG $COOKIE_ARG" + else + NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" + fi + SLIM_ARGS= +elif [ "$RUNNER_SCRIPT_DATA" = "slim" ]; then + # Setup system paths + SYSTEM_ERL_PATH=`which erl` + if [ ! -x "$SYSTEM_ERL_PATH" ]; then + echo "Failed to find erl. Is Erlang/OTP available in PATH?" + exit 1 + fi + SYSTEM_HOME_BIN=${SYSTEM_ERL_PATH%/*} + ROOTDIR=$SYSTEM_HOME_BIN/../lib/erlang + ERTS_PATH=$ROOTDIR/erts-$ERTS_VSN/bin + unset SYSTEM_ERL_PATH + unset SYSTEM_HOME_BIN + + LOCAL_ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin + NODETOOL="$ERTS_PATH/escript $RUNNER_BASE_DIR/releases/$APP_VSN/nodetool $NAME_ARG $COOKIE_ARG" + unset LOCAL_ERL_PATH + + # Setup additional arguments for slim release + SLIM_ARGS="-boot_var RELTOOL_EXT_LIB $RUNNER_BASE_DIR/lib -sasl releases_dir \"$RUNNER_BASE_DIR/releases\"" +else + echo "Unknown runner_script.data" + exit 1 +fi # Setup remote shell command to control node REMSH="$ERTS_PATH/erl -hidden $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG" @@ -310,11 +342,10 @@ case "$1" in ;; esac # Setup beam-required vars - ROOTDIR=$RUNNER_BASE_DIR BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` - CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH" + CMD="$BINDIR/erlexec $SLIM_ARGS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR @@ -339,11 +370,10 @@ case "$1" in FOREGROUNDOPTIONS="-noinput +Bd" # Setup beam-required vars - ROOTDIR=$RUNNER_BASE_DIR BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\///'` - CMD="$BINDIR/erlexec $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH" + CMD="$BINDIR/erlexec $SLIM_ARGS $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR diff --git a/rebar.config b/rebar.config index 9028737..bf4ef4f 100644 --- a/rebar.config +++ b/rebar.config @@ -4,7 +4,11 @@ %% escript_incl_extra is for internal rebar-private use only. %% Do not use outside rebar. Config interface is not stable. {escript_incl_extra, [{"priv/templates/*", "."}]}. -{erl_opts, [warnings_as_errors]}. +%% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17. +%% At the same time, their counterparts dict() and digraph() are to be deprecated +%% in Erlang 18. namespaced_types option is used to select proper type name +%% depending of the OTP version used. +{erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, warnings_as_errors]}. {xref_checks, []}. {xref_queries, [{"(XC - UC) || (XU - X - B diff --git a/src/rebar.erl b/src/rebar.erl index 618cce8..a43da5f 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -207,9 +207,18 @@ help() -> " ~p~n" " ~p~n" " ~p~n" - " ~p~n", + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + "Core command line options:~n" + " apps=app1,app2 (specify apps to process)~n" + " skip_apps=app1,app2 (specify apps to skip)~n", [ {recursive_cmds, []}, + {require_erts_vsn, ".*"}, + {require_otp_vsn, ".*"}, + {require_min_otp_vsn, ".*"}, {lib_dirs, []}, {sub_dirs, ["dir1", "dir2"]}, {plugins, [plugin1, plugin2]}, @@ -400,6 +409,9 @@ qc Test QuickCheck properties xref Run cross reference analysis +shell Start a shell similar to + 'erl -pa ebin -pa deps/*/ebin' + help Show the program options version Show version information ">>, diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 10c6483..1c90d22 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -39,13 +39,21 @@ -include("rebar.hrl"). +-ifdef(namespaced_types). +% dict:dict() exists starting from Erlang 17. +-type rebar_dict() :: dict:dict(). +-else. +% dict() has been obsoleted in Erlang 17 and deprecated in 18. +-type rebar_dict() :: dict(). +-endif. + -record(config, { dir :: file:filename(), opts = [] :: list(), - globals = new_globals() :: dict(), - envs = new_env() :: dict(), + globals = new_globals() :: rebar_dict(), + envs = new_env() :: rebar_dict(), %% cross-directory/-command config - skip_dirs = new_skip_dirs() :: dict(), - xconf = new_xconf() :: dict() }). + skip_dirs = new_skip_dirs() :: rebar_dict(), + xconf = new_xconf() :: rebar_dict() }). -export_type([config/0]). diff --git a/src/rebar_core.erl b/src/rebar_core.erl index 81b9a6d..3a4f205 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -88,7 +88,7 @@ process_commands([Command | Rest], ParentConfig) -> %% path from inside a subdirectory. true = rebar_utils:expand_code_path(), {ParentConfig2, _DirSet} = process_dir(rebar_utils:get_cwd(), - ParentConfig1, Command, + Command, ParentConfig1, sets:new()), case get_operations(ParentConfig2) of Operations -> @@ -117,18 +117,13 @@ process_commands([Command | Rest], ParentConfig) -> end, process_commands(Rest, ParentConfig4). -process_dir(Dir, ParentConfig, Command, DirSet) -> +process_dir(Dir, Command, ParentConfig, DirSet) -> case filelib:is_dir(Dir) of false -> ?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]), {ParentConfig, DirSet}; true -> - maybe_process_dir(Dir, ParentConfig, Command, DirSet) - end. - -maybe_process_dir(Dir, ParentConfig, Command, DirSet) -> - case should_cd_into_dir(Dir, ParentConfig, Command) of - true -> + WouldCd = would_cd_into_dir(Dir, Command, ParentConfig), ok = file:set_cwd(Dir), Config = maybe_load_local_config(Dir, ParentConfig), @@ -143,63 +138,89 @@ maybe_process_dir(Dir, ParentConfig, Command, DirSet) -> %% set of modules to process this dir. {ok, AvailModuleSets} = application:get_env(rebar, modules), ModuleSet = choose_module_set(AvailModuleSets, Dir), - skip_or_process_dir(ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet); + skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath, + ModuleSet, WouldCd) + end. + +would_cd_into_dir(Dir, Command, Config) -> + case would_cd_into_dir1(Dir, Command, Config) of + true -> + would_cd; false -> - {ParentConfig, DirSet} + would_not_cd end. -should_cd_into_dir(Dir, Config, Command) -> +would_cd_into_dir1(Dir, Command, Config) -> rebar_utils:processing_base_dir(Config, Dir) orelse rebar_config:is_recursive(Config) orelse - is_recursive_command(Config, Command). + is_recursive_command(Command, Config) orelse + is_generate_in_rel_dir(Command, Dir). -is_recursive_command(Config, Command) -> +%% Check whether the command is part of the built-in (or extended via +%% rebar.config) list of default-recursive commands. +is_recursive_command(Command, Config) -> {ok, AppCmds} = application:get_env(rebar, recursive_cmds), ConfCmds = rebar_config:get_local(Config, recursive_cmds, []), RecursiveCmds = AppCmds ++ ConfCmds, lists:member(Command, RecursiveCmds). -skip_or_process_dir({[], undefined}=ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) -> - process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet); -skip_or_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) -> - case lists:suffix(".app.src", ModuleSetFile) - orelse lists:suffix(".app", ModuleSetFile) of +%% If the directory we're about to process contains +%% reltool.config[.script] and the command to be applied is +%% 'generate', then it's safe to process. We do this to retain the +%% behavior of specifying {sub_dirs, ["rel"]} and have "rebar generate" +%% pick up rel/reltool.config[.script]. Without this workaround you'd +%% have to run "rebar -r generate" (which you don't want to do if you +%% have deps or other sub_dirs) or "cd rel && rebar generate". +is_generate_in_rel_dir(generate, Dir) -> + case rebar_rel_utils:is_rel_dir(Dir) of + {true, _} -> + true; + false -> + false + end; +is_generate_in_rel_dir(_, _) -> + false. + +skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath, + {[], undefined}=ModuleSet, WouldCd) -> + process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, + WouldCd); +skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath, + {_, File}=ModuleSet, WouldCd) -> + case lists:suffix(".app.src", File) + orelse lists:suffix(".app", File) of true -> %% .app or .app.src file, check if is_skipped_app - skip_or_process_dir1(ModuleSetFile, ModuleSet, - Config, CurrentCodePath, Dir, - Command, DirSet); + skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, + ModuleSet, WouldCd, File); false -> %% not an app dir, no need to consider apps=/skip_apps= - process_dir1(Dir, Command, DirSet, Config, - CurrentCodePath, ModuleSet) + process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, + ModuleSet, WouldCd) end. -skip_or_process_dir1(AppFile, ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) -> +skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, + WouldCd, AppFile) -> case rebar_app_utils:is_skipped_app(Config, AppFile) of {Config1, {true, _SkippedApp}} when Command == 'update-deps' -> %% update-deps does its own app skipping. Unfortunately there's no %% way to signal this to rebar_core, so we have to explicitly do it %% here... Otherwise if you use app=, it'll skip the toplevel %% directory and nothing will be updated. - process_dir1(Dir, Command, DirSet, Config1, - CurrentCodePath, ModuleSet); + process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath, + ModuleSet, WouldCd); {Config1, {true, SkippedApp}} -> ?DEBUG("Skipping app: ~p~n", [SkippedApp]), - Config2 = increment_operations(Config1), - {Config2, DirSet}; + {increment_operations(Config1), DirSet}; {Config1, false} -> - process_dir1(Dir, Command, DirSet, Config1, - CurrentCodePath, ModuleSet) + process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath, + ModuleSet, WouldCd) end. -process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, - {DirModules, ModuleSetFile}) -> +process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, + {DirModules, File}, WouldCd) -> Config0 = rebar_config:set_xconf(Config, current_command, Command), + %% Get the list of modules for "any dir". This is a catch-all list %% of modules that are processed in addition to modules associated %% with this directory type. These any_dir modules are processed @@ -210,8 +231,7 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, %% Invoke 'preprocess' on the modules -- this yields a list of other %% directories that should be processed _before_ the current one. - {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, - ModuleSetFile), + {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, File), %% Remember associated pre-dirs (used for plugin lookup) PredirsAssoc = remember_cwd_predirs(Dir, Predirs), @@ -219,55 +239,33 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, %% Get the list of plug-in modules from rebar.config. These %% modules may participate in preprocess and postprocess. {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc), + AllModules = Modules ++ PluginModules, - {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, - Config1, ModuleSetFile), + {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, Config1, + File), AllPredirs = Predirs ++ PluginPredirs, ?DEBUG("Predirs: ~p\n", [AllPredirs]), - {Config3, DirSet2} = process_each(AllPredirs, Command, Config2, - ModuleSetFile, DirSet), + {Config3, DirSet2} = process_each(AllPredirs, Command, Config2, DirSet, + File), %% Make sure the CWD is reset properly; processing the dirs may have %% caused it to change ok = file:set_cwd(Dir), - %% Check that this directory is not on the skip list - Config7 = case rebar_config:is_skip_dir(Config3, Dir) of - true -> - %% Do not execute the command on the directory, as some - %% module has requested a skip on it. - ?INFO("Skipping ~s in ~s\n", [Command, Dir]), - Config3; - - false -> - %% Check for and get command specific environments - {Config4, Env} = setup_envs(Config3, Modules), - - %% Execute any before_command plugins on this directory - Config5 = execute_pre(Command, PluginModules, - Config4, ModuleSetFile, Env), - - %% Execute the current command on this directory - Config6 = execute(Command, Modules ++ PluginModules, - Config5, ModuleSetFile, Env), - - %% Execute any after_command plugins on this directory - execute_post(Command, PluginModules, - Config6, ModuleSetFile, Env) - end, + %% Maybe apply command to Dir + Config4 = maybe_execute(Dir, Command, Config3, Modules, PluginModules, + AllModules, File, WouldCd), %% Mark the current directory as processed DirSet3 = sets:add_element(Dir, DirSet2), %% Invoke 'postprocess' on the modules. This yields a list of other %% directories that should be processed _after_ the current one. - {Config8, Postdirs} = acc_modules(Modules ++ PluginModules, postprocess, - Config7, ModuleSetFile), + {Config5, Postdirs} = acc_modules(AllModules, postprocess, Config4, File), ?DEBUG("Postdirs: ~p\n", [Postdirs]), - Res = process_each(Postdirs, Command, Config8, - ModuleSetFile, DirSet3), + Res = process_each(Postdirs, Command, Config5, DirSet3, File), %% Make sure the CWD is reset properly; processing the dirs may have %% caused it to change @@ -280,6 +278,33 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, %% Return the updated {config, dirset} as result Res. +maybe_execute(Dir, Command, Config, Modules, PluginModules, AllModules, File, + would_cd) -> + %% Check that this directory is not on the skip list + case rebar_config:is_skip_dir(Config, Dir) of + true -> + %% Do not execute the command on the directory, as some + %% module has requested a skip on it. + ?INFO("Skipping ~s in ~s\n", [Command, Dir]), + Config; + + false -> + %% Check for and get command specific environments + {Config1, Env} = setup_envs(Config, Modules), + + %% Execute any before_command plugins on this directory + Config2 = execute_pre(Command, PluginModules, Config1, File, Env), + + %% Execute the current command on this directory + Config3 = execute(Command, AllModules, Config2, File, Env), + + %% Execute any after_command plugins on this directory + execute_post(Command, PluginModules, Config3, File, Env) + end; +maybe_execute(_Dir, _Command, Config, _Modules, _PluginModules, _AllModules, + _File, would_not_cd) -> + Config. + remember_cwd_predirs(Cwd, Predirs) -> Store = fun(Dir, Dict) -> case dict:find(Dir, Dict) of @@ -310,21 +335,21 @@ maybe_load_local_config(Dir, ParentConfig) -> %% Given a list of directories and a set of previously processed directories, %% process each one we haven't seen yet %% -process_each([], _Command, Config, _ModuleSetFile, DirSet) -> +process_each([], _Command, Config, DirSet, _File) -> %% reset cached (setup_env) envs Config1 = rebar_config:reset_envs(Config), {Config1, DirSet}; -process_each([Dir | Rest], Command, Config, ModuleSetFile, DirSet) -> +process_each([Dir | Rest], Command, Config, DirSet, File) -> case sets:is_element(Dir, DirSet) of true -> ?DEBUG("Skipping ~s; already processed!\n", [Dir]), - process_each(Rest, Command, Config, ModuleSetFile, DirSet); + process_each(Rest, Command, Config, DirSet, File); false -> - {Config1, DirSet2} = process_dir(Dir, Config, Command, DirSet), + {Config1, DirSet2} = process_dir(Dir, Command, Config, DirSet), Config2 = rebar_config:clean_config(Config, Config1), %% reset cached (setup_env) envs Config3 = rebar_config:reset_envs(Config2), - process_each(Rest, Command, Config3, ModuleSetFile, DirSet2) + process_each(Rest, Command, Config3, DirSet2, File) end. %% @@ -358,20 +383,21 @@ execute_post(Command, Modules, Config, ModuleFile, Env) -> execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) -> HookFunction = list_to_atom(Hook ++ atom_to_list(Command)), - execute(HookFunction, Modules, Config, ModuleFile, Env). + execute(HookFunction, hook, Modules, Config, ModuleFile, Env). %% %% Execute a command across all applicable modules %% execute(Command, Modules, Config, ModuleFile, Env) -> + execute(Command, not_a_hook, Modules, Config, ModuleFile, Env). + +execute(Command, Type, Modules, Config, ModuleFile, Env) -> case select_modules(Modules, Command, []) of [] -> - Cmd = atom_to_list(Command), - case lists:prefix("pre_", Cmd) - orelse lists:prefix("post_", Cmd) of - true -> + case Type of + hook -> ok; - false -> + not_a_hook -> ?WARN("'~p' command does not apply to directory ~s\n", [Command, rebar_utils:get_cwd()]) end, diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 75d47fb..5f541d9 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -47,6 +47,14 @@ info = {[], []} :: erlc_info() }). +-ifdef(namespaced_types). +% digraph:digraph() exists starting from Erlang 17. +-type rebar_digraph() :: digraph:digraph(). +-else. +% digraph() has been obsoleted in Erlang 17 and deprecated in 18. +-type rebar_digraph() :: digraph(). +-endif. + %% =================================================================== %% Public API %% =================================================================== @@ -134,12 +142,13 @@ test_compile(Config, Cmd, OutDir) -> %% Obtain all the test modules for inclusion in the compile stage. TestErls = rebar_utils:find_files("test", ".*\\.erl\$"), + ErlOpts = rebar_utils:erl_opts(Config), + {Config1, ErlOpts1} = test_compile_config_and_opts(Config, ErlOpts, Cmd), + %% Copy source files to eunit dir for cover in case they are not directly %% in src but in a subdirectory of src. Cover only looks in cwd and ../src %% for source files. Also copy files from src_dirs. - ErlOpts = rebar_utils:erl_opts(Config), - - SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)), + SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts1)), SrcErls = lists:foldl( fun(Dir, Acc) -> Files = rebar_utils:find_files(Dir, ".*\\.erl\$"), @@ -172,8 +181,7 @@ test_compile(Config, Cmd, OutDir) -> %% Compile erlang code to OutDir, using a tweaked config %% with appropriate defines for eunit, and include all the test modules %% as well. - ok = doterl_compile(test_compile_config(Config, ErlOpts, Cmd), - OutDir, TestErls, ErlOpts), + ok = doterl_compile(Config1, OutDir, TestErls, ErlOpts1), {ok, SrcErls}. @@ -217,21 +225,22 @@ info_help(Description) -> {yrl_first_files, []} ]). -test_compile_config(Config, ErlOpts, Cmd) -> +test_compile_config_and_opts(Config, ErlOpts, Cmd) -> {Config1, TriqOpts} = triq_opts(Config), {Config2, PropErOpts} = proper_opts(Config1), {Config3, EqcOpts} = eqc_opts(Config2), OptsAtom = list_to_atom(Cmd ++ "_compile_opts"), - EunitOpts = rebar_config:get_list(Config3, OptsAtom, []), + TestOpts = rebar_config:get_list(Config3, OptsAtom, []), Opts0 = [{d, 'TEST'}] ++ - ErlOpts ++ EunitOpts ++ TriqOpts ++ PropErOpts ++ EqcOpts, + ErlOpts ++ TestOpts ++ TriqOpts ++ PropErOpts ++ EqcOpts, Opts = [O || O <- Opts0, O =/= no_debug_info], Config4 = rebar_config:set(Config3, erl_opts, Opts), FirstFilesAtom = list_to_atom(Cmd ++ "_first_files"), FirstErls = rebar_config:get_list(Config4, FirstFilesAtom, []), - rebar_config:set(Config4, erl_first_files, FirstErls). + Config5 = rebar_config:set(Config4, erl_first_files, FirstErls), + {Config5, Opts}. triq_opts(Config) -> {NewConfig, IsAvail} = is_lib_avail(Config, is_triq_avail, triq, @@ -408,17 +417,17 @@ init_erlcinfo(Config, Erls) -> update_erlcinfo(G, Source, Dirs) -> case digraph:vertex(G, Source) of {_, LastUpdated} -> - LastModified = filelib:last_modified(Source), - if LastModified == 0 -> + case filelib:last_modified(Source) of + 0 -> %% The file doesn't exist anymore, %% erase it from the graph. %% All the edges will be erased automatically. digraph:del_vertex(G, Source), modified; - LastUpdated < LastModified -> - modify_erlcinfo(G, Source, Dirs); + LastModified when LastUpdated < LastModified -> + modify_erlcinfo(G, Source, Dirs), modified; - true -> + _ -> unmodified end; false -> @@ -521,24 +530,24 @@ expand_file_names(Files, Dirs) -> end end, Files). --spec get_parents(digraph(), file:filename()) -> [file:filename()]. +-spec get_parents(rebar_digraph(), file:filename()) -> [file:filename()]. get_parents(G, Source) -> %% Return all files which the Source depends upon. digraph_utils:reachable_neighbours([Source], G). --spec get_children(digraph(), file:filename()) -> [file:filename()]. +-spec get_children(rebar_digraph(), file:filename()) -> [file:filename()]. get_children(G, Source) -> %% Return all files dependent on the Source. digraph_utils:reaching_neighbours([Source], G). -spec internal_erl_compile(rebar_config:config(), file:filename(), file:filename(), list(), - digraph()) -> 'ok' | 'skipped'. + rebar_digraph()) -> 'ok' | 'skipped'. internal_erl_compile(Config, Source, OutDir, ErlOpts, G) -> %% Determine the target name and includes list by inspecting the source file Module = filename:basename(Source, ".erl"), Parents = get_parents(G, Source), - log_files(?FMT("~s depends on", [Source]), Parents), + log_files(?FMT("Dependencies of ~s", [Source]), Parents), %% Construct the target filename Target = filename:join([OutDir | string:tokens(Module, ".")]) ++ ".beam", diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index fcd9c5e..9ddbf27 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -46,7 +46,7 @@ rm_rf(Target) -> {unix, _} -> EscTarget = escape_spaces(Target), {ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]), - [{use_stdout, false}, return_on_error]), + [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> Filelist = filelib:wildcard(Target), @@ -67,7 +67,7 @@ cp_r(Sources, Dest) -> SourceStr = string:join(EscSources, " "), {ok, []} = rebar_utils:sh(?FMT("cp -R ~s \"~s\"", [SourceStr, Dest]), - [{use_stdout, false}, return_on_error]), + [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> lists:foreach(fun(Src) -> ok = cp_r_win32(Src,Dest) end, Sources), @@ -81,7 +81,7 @@ mv(Source, Dest) -> EscSource = escape_spaces(Source), EscDest = escape_spaces(Dest), {ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), - [{use_stdout, false}, return_on_error]), + [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> {ok, R} = rebar_utils:sh( diff --git a/src/rebar_qc.erl b/src/rebar_qc.erl index 99d37a2..1976722 100644 --- a/src/rebar_qc.erl +++ b/src/rebar_qc.erl @@ -73,7 +73,10 @@ info(help, qc) -> [ {qc_compile_opts, []}, {qc_first_files, []} - ]). + ]); +info(help, clean) -> + Description = ?FMT("Delete QuickCheck test dir (~s)", [?QC_DIR]), + ?CONSOLE("~s.~n", [Description]). -define(TRIQ_MOD, triq). -define(EQC_MOD, eqc). diff --git a/src/rebar_rel_utils.erl b/src/rebar_rel_utils.erl index 085dbd9..5d99948 100644 --- a/src/rebar_rel_utils.erl +++ b/src/rebar_rel_utils.erl @@ -37,6 +37,7 @@ get_rel_file_path/2, load_config/2, get_sys_tuple/1, + get_excl_lib_tuple/1, get_target_dir/2, get_root_dir/2, get_target_parent_dir/2]). @@ -144,6 +145,13 @@ get_sys_tuple(ReltoolConfig) -> end. %% +%% Look for the {excl_lib, ...} tuple in sys tuple of the reltool.config file. +%% Without this present, return false. +%% +get_excl_lib_tuple(ReltoolConfig) -> + lists:keyfind(excl_lib, 1, element(2, get_sys_tuple(ReltoolConfig))). + +%% %% Look for {target_dir, TargetDir} in the reltool config file; if none is %% found, use the name of the release as the default target directory. %% diff --git a/src/rebar_reltool.erl b/src/rebar_reltool.erl index 9f9488e..fdaa7e0 100644 --- a/src/rebar_reltool.erl +++ b/src/rebar_reltool.erl @@ -147,15 +147,12 @@ process_overlay(Config, ReltoolConfig) -> OverlayVars1), %% Finally, overlay the files specified by the overlay section - case lists:keyfind(overlay, 1, ReltoolConfig) of - {overlay, Overlay} when is_list(Overlay) -> + case overlay_files(ReltoolConfig) of + [] -> + ok; + Overlay -> execute_overlay(Overlay, OverlayVars, rebar_utils:get_cwd(), - TargetDir); - false -> - ?INFO("No {overlay, [...]} found in reltool.config.\n", []); - _ -> - ?ABORT("{overlay, [...]} entry in reltool.config " - "must be a list.\n", []) + TargetDir) end. %% @@ -292,6 +289,26 @@ dump_spec(Config, Spec) -> end. +overlay_files(ReltoolConfig) -> + Original = case lists:keyfind(overlay, 1, ReltoolConfig) of + {overlay, Overlay} when is_list(Overlay) -> + Overlay; + false -> + ?INFO("No {overlay, [...]} found in reltool.config.\n", []), + []; + _ -> + ?ABORT("{overlay, [...]} entry in reltool.config " + "must be a list.\n", []) + end, + SlimAddition = case rebar_rel_utils:get_excl_lib_tuple(ReltoolConfig) of + {excl_lib, otp_root} -> + [{create, "releases/{{rel_vsn}}/runner_script.data", + "slim\n"}]; + false -> + [] + end, + Original ++ SlimAddition. + %% TODO: Merge functionality here with rebar_templater execute_overlay([], _Vars, _BaseDir, _TargetDir) -> diff --git a/src/rebar_require_vsn.erl b/src/rebar_require_vsn.erl index 385f55c..af805c8 100644 --- a/src/rebar_require_vsn.erl +++ b/src/rebar_require_vsn.erl @@ -34,7 +34,8 @@ eunit/2]). %% for internal use only --export([info/2]). +-export([info/2, + version_tuple/2]). %% =================================================================== %% Public API @@ -110,7 +111,7 @@ check_versions(Config) -> end. version_tuple(OtpRelease, Type) -> - case re:run(OtpRelease, "R(\\d+)B?-?(\\d+)?", [{capture, all, list}]) of + case re:run(OtpRelease, "R?(\\d+)B?-?(\\d+)?", [{capture, all, list}]) of {match, [_Full, Maj, Min]} -> {list_to_integer(Maj), list_to_integer(Min)}; {match, [_Full, Maj]} -> diff --git a/src/rebar_shell.erl b/src/rebar_shell.erl index 2dbf4a0..348e540 100644 --- a/src/rebar_shell.erl +++ b/src/rebar_shell.erl @@ -30,27 +30,40 @@ -include("rebar.hrl"). --export([shell/2]). +-export([shell/2, info/2]). + +%% NOTE: +%% this is an attempt to replicate `erl -pa ./ebin -pa deps/*/ebin`. it is +%% mostly successful but does stop and then restart the user io system to get +%% around issues with rebar being an escript and starting in `noshell` mode. +%% it also lacks the ctrl-c interrupt handler that `erl` features. ctrl-c will +%% immediately kill the script. ctrl-g, however, works fine shell(_Config, _AppFile) -> - ?CONSOLE("NOTICE: Using experimental 'shell' command~n", []), - %% backwards way to say we only want this executed - %% for the "top level" directory - case is_deps_dir(rebar_utils:get_cwd()) of - false -> - true = code:add_pathz(rebar_utils:ebin_dir()), - user_drv:start(), - %% this call never returns (until user quits shell) - shell:server(false, false); - true -> - ok - end, - ok. + true = code:add_pathz(rebar_utils:ebin_dir()), + %% terminate the current user + ok = supervisor:terminate_child(kernel_sup, user), + %% start a new shell (this also starts a new user under the correct group) + user_drv:start(), + %% enable error_logger's tty output + ok = error_logger:swap_handler(tty), + %% disable the simple error_logger (which may have been added multiple + %% times). removes at most the error_logger added by init and the + %% error_logger added by the tty handler + ok = remove_error_handler(3), + %% this call never returns (until user quits shell) + timer:sleep(infinity). + +info(help, shell) -> + ?CONSOLE( + "Start a shell with project and deps preloaded similar to~n" + "'erl -pa ebin -pa deps/*/ebin'.~n", + []). -is_deps_dir(Dir) -> - case lists:reverse(filename:split(Dir)) of - [_, "deps" | _] -> - true; - _V -> - false - end. +remove_error_handler(0) -> + ?WARN("Unable to remove simple error_logger handler~n", []); +remove_error_handler(N) -> + case gen_event:delete_handler(error_logger, error_logger, []) of + {error, module_not_found} -> ok; + {error_logger, _} -> remove_error_handler(N-1) + end.
\ No newline at end of file diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 6b8e874..c02d200 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -335,7 +335,8 @@ processing_base_dir(Config) -> processing_base_dir(Config, Cwd). processing_base_dir(Config, Dir) -> - Dir =:= base_dir(Config). + AbsDir = filename:absname(Dir), + AbsDir =:= base_dir(Config). %% ==================================================================== %% Internal functions @@ -417,8 +418,9 @@ log_msg_and_abort(Message) -> -spec log_and_abort(string(), {integer(), string()}) -> no_return(). log_and_abort(Command, {Rc, Output}) -> - ?ABORT("~s failed with error: ~w and output:~n~s~n", - [Command, Rc, Output]). + ?ABORT("sh(~s)~n" + "failed with return code ~w and the following output:~n" + "~s~n", [Command, Rc, Output]). sh_loop(Port, Fun, Acc) -> receive diff --git a/test/rebar_file_utils_tests.erl b/test/rebar_file_utils_tests.erl index 26a6f9f..a191765 100644 --- a/test/rebar_file_utils_tests.erl +++ b/test/rebar_file_utils_tests.erl @@ -191,7 +191,7 @@ cp_r_overwrite_file_fail_test_() -> filename:join([?TMP_DIR,"dest","file1"]),0) end, fun teardown/1, - [?_assertError({badmatch,_}, + [?_assertThrow(rebar_abort, rebar_file_utils:cp_r( [filename:join([?TMP_DIR,"source","file1"])], filename:join([?TMP_DIR,"dest"])))]}. @@ -210,7 +210,7 @@ cp_r_overwrite_dir_fail_test_() -> filename:join([?TMP_DIR,"dest","source","file1"]),0) end, fun teardown/1, - [?_assertError({badmatch,_}, + [?_assertThrow(rebar_abort, rebar_file_utils:cp_r( [filename:join([?TMP_DIR,"source"])], filename:join([?TMP_DIR,"dest"])))]}. diff --git a/test/rebar_require_vsn_tests.erl b/test/rebar_require_vsn_tests.erl new file mode 100644 index 0000000..2d3a1ec --- /dev/null +++ b/test/rebar_require_vsn_tests.erl @@ -0,0 +1,23 @@ +-module(rebar_require_vsn_tests). + +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +version_tuple_test_() -> + [%% typical cases + ?_assert(rebar_require_vsn:version_tuple("R15B", "eunit") =:= {15, 0}), + ?_assert(rebar_require_vsn:version_tuple("R15B01", "eunit") =:= {15, 1}), + ?_assert(rebar_require_vsn:version_tuple("R15B02", "eunit") =:= {15, 2}), + ?_assert(rebar_require_vsn:version_tuple("R15B03-1", "eunit") =:= {15, 3}), + ?_assert(rebar_require_vsn:version_tuple("R15B03", "eunit") =:= {15, 3}), + ?_assert(rebar_require_vsn:version_tuple("R16B", "eunit") =:= {16, 0}), + ?_assert(rebar_require_vsn:version_tuple("R16B01", "eunit") =:= {16, 1}), + ?_assert(rebar_require_vsn:version_tuple("R16B02", "eunit") =:= {16, 2}), + ?_assert(rebar_require_vsn:version_tuple("R16B03", "eunit") =:= {16, 3}), + ?_assert(rebar_require_vsn:version_tuple("R16B03-1", "eunit") =:= {16, 3}), + ?_assert(rebar_require_vsn:version_tuple("17", "eunit") =:= {17, 0}), + %% error cases + ?_assertException(throw, rebar_abort, rebar_require_vsn:version_tuple("", "eunit")), + ?_assertException(throw, rebar_abort, rebar_require_vsn:version_tuple("abc", "eunit")) + ]. |
