diff options
47 files changed, 892 insertions, 493 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0de0eb..0ce1e93 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ Contributing to rebar --------------------- -Before implementing a new feature, please submit a ticket to discuss your plans. +Before implementing a new feature, please submit a ticket to discuss your plans. The feature might have been rejected already, or the implementation might already be decided. See [Community and Resources](README.md#community-and-resources). @@ -9,95 +9,99 @@ See [Community and Resources](README.md#community-and-resources). Code style ---------- -The following rules must be followed: +The following rules apply: * Do not introduce trailing whitespace - * Do not mix spaces and tabs - * Do not introduce lines longer than 80 characters - -The following rules should be followed: + * We use spaces for indenting only + * Try not to introduce lines longer than 80 characters * Write small functions whenever possible - * Avoid having too many clauses containing clauses containing clauses. + * Avoid having too many clauses containing clauses containing clauses. Basically, avoid deeply nested functions. -[erlang-mode (emacs)](http://www.erlang.org/doc/man/erlang.el.html) -indentation is preferred. This will keep the code base consistent. -vi users are encouraged to give [Vim emulation](http://emacswiki.org/emacs/Evil) ([more -info](https://gitorious.org/evil/pages/Home)) a try. +Follow the indentation style of existing files. The [erlang-mode for +(emacs)](http://www.erlang.org/doc/man/erlang.el.html) indentation is going to +always work. Other users may want to use 4-width spaces and make sure things +align mostly the way they would with Emacs code, or with the rest of the +project. + +Where possible, include type specifications for your code so type analysis +will be as accurate as possible. + +Please add comments around tricky fixes or workarounds so that we can +easily know why they're there at a glance. Pull requests and branching --------------------------- -Use one topic branch per pull request. If you do that, you can add extra commits or fix up -buggy commits via `git rebase -i`, and update the branch. The updated branch will be -visible in the same pull request. Therefore, you should not open a new pull request when -you have to fix your changes. +All fixes to rebar end up requiring a +1 from one or more of the project's +maintainers. When opening a pull request, explain what the patch is doing +and if it makes sense, why the proposed implementation was chosen. + +Try to use well-defined commits (one feature per commit) so that reading +them and testing them is easier for reviewers and while bissecting the code +base for issues. + +During the review process, you may be asked to correct or edit a few things +before a final rebase to merge things. -Do not commit to master in your fork. +Please work in feature branches, and do not commit to `master` in your fork. Provide a clean branch without merge commits. Tests ----- -As a general rule, any behavioral change to rebar requires a test to go with it. If there's -already a test case, you may have to modify that one. If there isn't a test case or a test -suite, add a new test case or suite in `inttest/`. [retest](https://github.com/dizzyd/retest) based tests are preferred, but -we also have EUnit tests in `test/`. +As a general rule, any behavioral change to rebar requires a test to go with +it. If there's already a test case, you may have to modify that one. If there +isn't a test case or a test suite, add a new test case or suite in `test/`. -Say you've added a new test case in `inttest/erlc`. To only execute the modified suite, -you would do the following: -```sh -# First we build rebar and its deps to also get `deps/retest/retest` -$ make debug deps -# Now we can test the modified erlc suite -$ deps/retest/retest -v inttest/erlc -``` +To run the tests: -To test EUnit tests, you would do: ```sh -$ make debug -$ ./rebar -v eunit +$ ./bootstrap +$ ./rebar3 ct ``` -You can also run `make test` to execute both EUnit and [retest](https://github.com/dizzyd/retest) tests as `make check` does. +The rebar3 test suite is written using Common Test. As many of the tests as +possible are written by using the programmatic rebar3 API rather than +by running the escriptized project directly. The tests should be restricted +to their `priv_dir` and leave the system clean after a run. + + If such tests prove hard to write, you can ask for help doing that in your +pull request. + +For tests having a lot to do with I/O and terminal interaction, consider +adding them to https://github.com/tsloughter/rebar3_tests + Credit ------ -To give everyone proper credit in addition to the git history, please feel free to append +To give everyone proper credit in addition to the git history, please feel free to append your name to `THANKS` in your first contribution. Committing your changes ----------------------- -Please ensure that all commits pass all tests, and do not have extra Dialyzer warnings. -To do that run `make check`. If you didn't build via `make debug` at first, the beam files in -`ebin/` might be missing debug_info required for [xref](http://www.erlang.org/doc/man/xref.html) -and [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html), causing a test -failure. -If that happens, running `make clean` before running `make check` could solve the problem. -If you change any of the files with known but safe to ignore Dialyzer warnings, you may -have to adapt the line number(s) in [dialyzer_reference](dialyzer_reference). If you do that, -do not remove the -leading blank line. +Please ensure that all commits pass all tests, and do not have extra Dialyzer warnings. +To do that run `./rebar3 ct` and `./rebar3 as dialyze dialyzer`. #### Structuring your commits -Fixing a bug is one commit. -Adding a feature is one commit. -Adding two features is two commits. -Two unrelated changes is two commits. +- Fixing a bug is one commit. +- Adding a feature is one commit. +- Adding two features is two commits. +- Two unrelated changes is two commits (and likely two Pull requests) -If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup commit into +If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup commit into the original commit. #### Writing Commit Messages -It's important to write a proper commit title and description. The commit title must be -at most 50 characters; it is the first line of the commit text. The second line of the -commit text must be left blank. The third line and beyond is the commit message. You -should write a commit message. If you do, wrap all lines at 72 characters. You should -explain what the commit does, what references you used, and any other information +It's important to write a proper commit title and description. The commit title must be +at most 50 characters; it is the first line of the commit text. The second line of the +commit text must be left blank. The third line and beyond is the commit message. You +should write a commit message. If you do, wrap all lines at 72 characters. You should +explain what the commit does, what references you used, and any other information that helps understanding your changes. Basically, structure your commit message like this: @@ -11,21 +11,13 @@ embed directly in a project. Where possible, rebar uses standard Erlang/OTP conventions for project structures, thus minimizing the amount of build configuration work. rebar also provides dependency management, enabling application writers to easily re-use common libraries from a variety of -locations (git, hg, etc). +locations ([hex.pm](http://hex.pm), git, hg, and so on). -3.0 Alpha-6 +3.0 Beta-1 ==== [DOCUMENTATION](http://www.rebar3.org/v3.0/docs) -This is a preliminary branch, considered to be alpha, and still -very unstable. Use at your own risk, but please do report bugs -and issues encountered and we'll try to resolve problems as -soon as possible. - -Compatibility with rebar 2.0 has been broken, and features removed to -limit scope. - ### Commands | Command | Description | @@ -35,6 +27,7 @@ limit scope. | clean | Remove project apps beam files | | cover | Generate coverage info from data compiled by `eunit --cover` or `ct --cover` | | ct | Run Common Test suites | +| deps | Lists dependencies currently in use | | do | Higher-order provider to run multiple tasks in sequence | | dialyzer | Run the Dialyzer analyzer on the project | | edoc | Generate documentation using edoc | @@ -55,15 +48,26 @@ limit scope. | version | Print current version of Erlang/OTP and rebar | | xref | Run cross reference analysis on the project | +A more complete list can be found on the [docs page](http://www.rebar3.org/v3.0/docs/commands) + ### Changes +#### Since Rebar 2.x + * Fetches and builds deps if missing when running any command that relies on them * Automatically recognizes `apps` and `lib` directory structure -* Relx for releases +* Relx for releases and relups +* deterministic builds and conflict resolution +* New plugin handling mechanism +* New hook mechanism +* Support for packages +* A ton more ### Gone * Reltool integeration +* Quickcheck integration (moved to [a plugin](http://www.rebar3.org/v3.0/docs/using-available-plugins#quickcheck)) +* C code compiling (moved to [a plugin](http://www.rebar3.org/v3.0/docs/using-available-plugins#port-compiler) or hooks) ### Providers @@ -144,7 +148,6 @@ $ ./rebar3 escriptize $ _build/default/bin/rebar3 ``` - Contributing to rebar ===================== @@ -174,3 +177,4 @@ General rebar community resources and links: - #rebar on [irc.freenode.net](http://freenode.net/) - [wiki](https://github.com/rebar/rebar/wiki) - [issues](https://github.com/rebar/rebar/issues) +- [Documentation](http://www.rebar3.org/v3.0/docs) @@ -134,3 +134,4 @@ Martin Karlsson Pierre Fenoll David Kubecka Stefan Grundmann +Carlos Eduardo de Paula @@ -9,6 +9,8 @@ main(_Args) -> application:start(public_key), application:start(ssl), inets:start(), + inets:start(httpc, [{profile, rebar}]), + set_httpc_options(), %% Fetch and build deps required to build rebar3 BaseDeps = [{providers, []} @@ -84,13 +86,38 @@ extract(Binary) -> request(Url) -> case httpc:request(get, {Url, []}, [{relaxed, true}], - [{body_format, binary}]) of + [{body_format, binary}], + rebar) of {ok, {{_Version, 200, _Reason}, _Headers, Body}} -> {ok, Body}; Error -> Error end. +get_rebar_config() -> + {ok, [[Home]]} = init:get_argument(home), + ConfDir = filename:join(Home, ".config/rebar3"), + case file:consult(filename:join(ConfDir, "rebar.config")) of + {ok, Config} -> + Config; + _ -> + [] + end. + +get_http_vars(Scheme) -> + proplists:get_value(Scheme, get_rebar_config(), []). + +set_httpc_options() -> + set_httpc_options(https_proxy, get_http_vars(https_proxy)), + set_httpc_options(proxy, get_http_vars(http_proxy)). + +set_httpc_options(_, []) -> + ok; + +set_httpc_options(Scheme, Proxy) -> + {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), + httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). + compile(App, FirstFiles) -> Dir = filename:join(filename:absname("_build/default/lib/"), App), filelib:ensure_dir(filename:join([Dir, "ebin", "dummy.beam"])), diff --git a/priv/templates/otp_app.app.src b/priv/templates/otp_app.app.src index 9ad7478..09e4a48 100644 --- a/priv/templates/otp_app.app.src +++ b/priv/templates/otp_app.app.src @@ -8,5 +8,9 @@ stdlib ]}, {env,[]}, - {modules, []} + {modules, []}, + + {contributors, []}, + {licenses, []}, + {links, []} ]}. diff --git a/priv/templates/otp_lib.app.src b/priv/templates/otp_lib.app.src index 3f4b56b..f07293e 100644 --- a/priv/templates/otp_lib.app.src +++ b/priv/templates/otp_lib.app.src @@ -7,5 +7,9 @@ stdlib ]}, {env,[]}, - {modules, []} + {modules, []}, + + {contributors, []}, + {licenses, []}, + {links, []} ]}. diff --git a/priv/templates/plugin.erl b/priv/templates/plugin.erl index 018dd0e..c6e5e40 100644 --- a/priv/templates/plugin.erl +++ b/priv/templates/plugin.erl @@ -1,33 +1,8 @@ -module('{{name}}'). --behaviour(provider). --export([init/1, do/1, format_error/1]). +-export([init/1]). --define(PROVIDER, '{{name}}'). --define(DEPS, [app_discovery]). - -%% =================================================================== -%% Public API -%% =================================================================== -spec init(rebar_state:t()) -> {ok, rebar_state:t()}. init(State) -> - Provider = providers:create([ - {name, ?PROVIDER}, % The 'user friendly' name of the task - {module, ?MODULE}, % The module implementation of the task - {bare, true}, % The task can be run by the user, always true - {deps, ?DEPS}, % The list of dependencies - {example, "rebar3 {{name}}"}, % How to use the plugin - {opts, []}, % list of options understood by the plugin - {short_desc, "{{desc}}"}, - {desc, "{{desc}}"} - ]), - {ok, rebar_state:add_provider(State, Provider)}. - - --spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. -do(State) -> - {ok, State}. - --spec format_error(any()) -> iolist(). -format_error(Reason) -> - io_lib:format("~p", [Reason]). + {ok, State1} = '{{name}}_prv':init(State), + {ok, State1}. diff --git a/priv/templates/plugin.template b/priv/templates/plugin.template index 811be0b..c0e36de 100644 --- a/priv/templates/plugin.template +++ b/priv/templates/plugin.template @@ -4,6 +4,7 @@ {desc, "A rebar plugin", "Short description of the plugin's purpose"} ]}. {template, "plugin.erl", "{{name}}/src/{{name}}.erl"}. +{template, "provider.erl", "{{name}}/src/prv_{{name}}_prv.erl"}. {template, "otp_lib.app.src", "{{name}}/src/{{name}}.app.src"}. {template, "rebar.config", "{{name}}/rebar.config"}. {template, "gitignore", "{{name}}/.gitignore"}. diff --git a/priv/templates/provider.erl b/priv/templates/provider.erl new file mode 100644 index 0000000..669df83 --- /dev/null +++ b/priv/templates/provider.erl @@ -0,0 +1,32 @@ +-module('{{name}}_prv'). + +-export([init/1, do/1, format_error/1]). + +-define(PROVIDER, '{{name}}'). +-define(DEPS, [app_discovery]). + +%% =================================================================== +%% Public API +%% =================================================================== +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + Provider = providers:create([ + {name, ?PROVIDER}, % The 'user friendly' name of the task + {module, ?MODULE}, % The module implementation of the task + {bare, true}, % The task can be run by the user, always true + {deps, ?DEPS}, % The list of dependencies + {example, "rebar3 {{name}}"}, % How to use the plugin + {opts, []}, % list of options understood by the plugin + {short_desc, "{{desc}}"}, + {desc, "{{desc}}"} + ]), + {ok, rebar_state:add_provider(State, Provider)}. + + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(State) -> + {ok, State}. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). diff --git a/rebar.config b/rebar.config index bd0f517..16b57d8 100644 --- a/rebar.config +++ b/rebar.config @@ -1,12 +1,11 @@ %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -{deps, [ - {erlware_commons, "0.12.0"}, - {providers, "1.4.1"}, - {getopt, "0.8.2"}, - {bbmustache, "1.0.1"}, - {relx, "3.0.0"}]}. +{deps, [{erlware_commons, "0.13.0"}, + {providers, "1.4.1"}, + {getopt, "0.8.2"}, + {bbmustache, "1.0.3"}, + {relx, "3.3.0"}]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. @@ -16,20 +15,18 @@ {"priv/templates/*", "."}, {"rebar/include/*", "."}]}. -{erl_opts, - [{platform_define, "^[0-9]+", namespaced_types}, - no_debug_info, - warnings_as_errors]}. +{erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + no_debug_info, + warnings_as_errors]}. {dialyzer_plt_apps, [common_test, dialyzer, eunit, snmp]}. %% Profiles -{profiles, [{test, - [{deps, [ - {meck, "", {git, "https://github.com/eproxus/meck.git", {tag, "0.8.2"}}} - ]}, - {erl_opts, [debug_info]} - ]}, +{profiles, [{test, [ + {deps, [{meck, "0.8.2"}]}, + {erl_opts, [debug_info]} + ] + }, {bootstrap, []}, diff --git a/rebar.config.sample b/rebar.config.sample index f6d27c8..1bd5e0c 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -83,14 +83,14 @@ %% == Cover == -%% Whether to enable coverage reporting. Default is `false' +%% Whether to enable coverage reporting where commands support cover. Default +%% is `false' {cover_enabled, false}. -%% Whether to print coverage report to console. Default is `false' -{cover_print_enabled, false}. +%% Options to pass to cover provider +{cover_opts, [verbose]}. -%% Directory to store collected cover data -{cover_data_dir, "cover"}. +%% == Dependencies == %% What dependencies we have, dependencies can be of 3 forms, an application %% name as an atom, eg. mochiweb, a name and a version (from the .app file), or @@ -1,5 +1,5 @@ -[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.1">>},0}, +[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.3">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.4.1">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.12.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.0.0">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.13.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.3.0">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}]. diff --git a/src/rebar.app.src b/src/rebar.app.src index 5abb643..e5d56bb 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.0.0-alpha-6"}, + {vsn, "3.0.0-beta-1"}, {modules, []}, {registered, []}, {applications, [kernel, diff --git a/src/rebar3.erl b/src/rebar3.erl index 5c0559f..a797c46 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -272,4 +272,7 @@ start_and_load_apps() -> application:start(asn1), application:start(public_key), application:start(ssl), - inets:start(). + inets:start(), + inets:start(httpc, [{profile, rebar}]), + rebar_utils:set_httpc_options(). + diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 0432fb8..dc45dcf 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -20,17 +20,17 @@ do(Namespace, Command) when is_atom(Namespace), is_atom(Command) -> gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity). init(State) -> - {ok, Cwd} = file:get_cwd(), + Cwd = rebar_dir:get_cwd(), {ok, #state{state=State, cwd=Cwd}}. handle_call({cmd, Command}, _From, State=#state{state=RState, cwd=Cwd}) -> MidState = maybe_show_warning(State), {Res, NewRState} = run(default, Command, RState, Cwd), - {reply, Res, MidState#state{state=NewRState}}; + {reply, Res, MidState#state{state=NewRState}, hibernate}; handle_call({cmd, Namespace, Command}, _From, State = #state{state=RState, cwd=Cwd}) -> MidState = maybe_show_warning(State), {Res, NewRState} = run(Namespace, Command, RState, Cwd), - {reply, Res, MidState#state{state=NewRState}}; + {reply, Res, MidState#state{state=NewRState}, hibernate}; handle_call(_Call, _From, State) -> {noreply, State}. @@ -48,8 +48,8 @@ terminate(_Reason, _State) -> run(Namespace, Command, RState, Cwd) -> try - case file:get_cwd() of - {ok, Cwd} -> + case rebar_dir:get_cwd() of + Cwd -> Args = [atom_to_list(Namespace), atom_to_list(Command)], CmdState0 = refresh_state(RState, Cwd), CmdState1 = rebar_state:set(CmdState0, task, atom_to_list(Command)), @@ -87,16 +87,22 @@ refresh_paths(RState) -> %% make sure to never reload self; halt()s the VM ) -- [filename:dirname(code:which(?MODULE))], %% Similar to rebar_utils:update_code/1, but also forces a reload - %% of used modules. + %% of used modules. Also forces to reload all of ebin/ instead + %% of just the modules in the .app file, because 'extra_src_dirs' + %% allows to load and compile files that are not to be kept + %% in the app file. lists:foreach(fun(Path) -> Name = filename:basename(Path, "/ebin"), + Files = filelib:wildcard(filename:join([Path, "*.beam"])), + Modules = [list_to_atom(filename:basename(F, ".beam")) + || F <- Files], App = list_to_atom(Name), application:load(App), case application:get_key(App, modules) of undefined -> code:add_patha(Path), ok; - {ok, Modules} -> + {ok, _} -> ?DEBUG("reloading ~p from ~s", [Modules, Path]), code:replace_path(Name, Path), [begin code:purge(M), code:delete(M), code:load_file(M) end @@ -108,5 +114,5 @@ refresh_state(RState, _Dir) -> lists:foldl( fun(F, State) -> F(State) end, rebar3:init_config(), - [fun(S) -> rebar_state:current_profiles(S, rebar_state:current_profiles(RState)) end] + [fun(S) -> rebar_state:apply_profiles(S, rebar_state:current_profiles(RState)) end] ). diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index 2eb9d91..9c4a5ff 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -139,7 +139,7 @@ app_dir(AppFile) -> -spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}. create_app_info(AppDir, AppFile) -> - [{application, AppName, AppDetails}] = rebar_file_utils:try_consult(AppFile), + [{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile), AppVsn = proplists:get_value(vsn, AppDetails), Applications = proplists:get_value(applications, AppDetails, []), IncludedApplications = proplists:get_value(included_applications, AppDetails, []), diff --git a/src/rebar_core.erl b/src/rebar_core.erl index b1647f0..f097429 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -128,9 +128,15 @@ do([ProviderName | Rest], State) -> {error, Error} catch error:undef -> - %% This should really only happen if a plugin provider doesn't export do/1 - ?DEBUG("Undefined call to provider's do function:~n~p", [erlang:get_stacktrace()]), - ?PRV_ERROR({bad_provider_namespace, ProviderName}); + Stack = erlang:get_stacktrace(), + case Stack of + [{ProviderName, do, [_], _}|_] -> + %% This should really only happen if a plugin provider doesn't export do/1 + ?DEBUG("Undefined call to provider's do/1 function:~n~p", [Stack]), + ?PRV_ERROR({bad_provider_namespace, ProviderName}); + _ -> % re-raise + erlang:raise(error, undef, Stack) + end; error:{badrecord,provider} -> {error, ProviderName} end. diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index e226633..7af94ea 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -100,7 +100,11 @@ local_cache_dir(Dir) -> get_cwd() -> {ok, Dir} = file:get_cwd(), - Dir. + %% On windows cwd may return capital letter for drive, + %% for example C:/foobar. But as said in http://www.erlang.org/doc/man/filename.html#join-1 + %% filename:join/1,2 anyway will convert drive-letter to lowercase, so we have to "internalize" + %% cwd as soon as it possible. + filename:join([Dir]). template_globals(State) -> filename:join([global_config_dir(State), "templates", "globals"]). diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index b9072a3..87cf352 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -404,10 +404,13 @@ target_base(OutDir, Source) -> -spec compile_mib(file:filename(), file:filename(), rebar_state:t()) -> 'ok'. compile_mib(Source, Target, Config) -> + Dir = rebar_state:dir(Config), ok = filelib:ensure_dir(Target), - ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")), - Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++ + ok = filelib:ensure_dir(filename:join([Dir, "include", "dummy.hrl"])), + Opts = [{outdir, filename:join([Dir, "priv", "mibs"])} + ,{i, [filename:join([Dir, "priv", "mibs"])]}] ++ rebar_state:get(Config, mib_opts, []), + case snmpc:compile(Source, Opts) of {ok, _} -> Mib = filename:rootname(Target), diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index ad30172..b4cdc27 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -36,10 +36,14 @@ write_file_if_contents_differ/2, system_tmpdir/0, system_tmpdir/1, - reset_dir/1]). + reset_dir/1, + touch/1]). -include("rebar.hrl"). + -include_lib("providers/include/providers.hrl"). +-include_lib("kernel/include/file.hrl"). + %% =================================================================== %% Public API @@ -71,9 +75,40 @@ symlink_or_copy(Source, Target) -> {error, eexist} -> ok; {error, _} -> - cp_r([Source], Target) + case os:type() of + {win32, _} -> + S = unicode:characters_to_list(Source), + T = unicode:characters_to_list(Target), + case filelib:is_dir(S) of + true -> + win32_symlink(S, T); + false -> + cp_r([S], T) + end; + _ -> + case filelib:is_dir(Target) of + true -> + ok; + false -> + cp_r([Source], Target) + end + end + end. + +win32_symlink(Source, Target) -> + Res = rebar_utils:sh( + ?FMT("cmd /c mklink /j \"~s\" \"~s\"", + [filename:nativename(Target), filename:nativename(Source)]), + [{use_stdout, false}, return_on_error]), + case win32_ok(Res) of + true -> ok; + false -> + {error, lists:flatten( + io_lib:format("Failed to symlink ~s to ~s~n", + [Source, Target]))} end. + %% @doc Remove files and directories. %% Target is a single filename, directoryname or wildcard expression. -spec rm_rf(string()) -> 'ok'. @@ -120,21 +155,32 @@ mv(Source, Dest) -> [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> - {ok, R} = rebar_utils:sh( - ?FMT("move /y \"~s\" \"~s\" 1> nul", - [filename:nativename(Source), - filename:nativename(Dest)]), + Cmd = case filelib:is_dir(Source) of + true -> + ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul", + [filename:nativename(Source), + filename:nativename(Dest)]); + false -> + ?FMT("robocopy /move /s \"~s\" \"~s\" \"~s\" 1> nul", + [filename:nativename(filename:dirname(Source)), + filename:nativename(Dest), + filename:basename(Source)]) + end, + Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), - case R of - [] -> - ok; - _ -> + case win32_ok(Res) of + true -> ok; + false -> {error, lists:flatten( io_lib:format("Failed to move ~s to ~s~n", [Source, Dest]))} end end. +win32_ok({ok, _}) -> true; +win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true; +win32_ok(_) -> false. + delete_each([]) -> ok; delete_each([File | Rest]) -> @@ -186,6 +232,17 @@ reset_dir(Path) -> %% recreate the directory filelib:ensure_dir(filename:join([Path, "dummy.beam"])). + +%% Linux touch but using erlang functions to work in bot *nix os and +%% windows +-spec touch(Path) -> ok | {error, Reason} when + Path :: file:name(), + Reason :: file:posix(). +touch(Path) -> + {ok, A} = file:read_file_info(Path), + ok = file:write_file_info(Path, A#file_info{mtime = calendar:local_time(), + atime = calendar:local_time()}). + %% =================================================================== %% Internal functions %% =================================================================== @@ -198,28 +255,36 @@ delete_each_dir_win32([Dir | Rest]) -> delete_each_dir_win32(Rest). xcopy_win32(Source,Dest)-> - {ok, R} = rebar_utils:sh( - ?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul", - [filename:nativename(Source), filename:nativename(Dest)]), + %% "xcopy \"~s\" \"~s\" /q /y /e 2> nul", Chanegd to robocopy to + %% handle long names. May have issues with older windows. + Cmd = case filelib:is_dir(Source) of + true -> + ?FMT("robocopy \"~s\" \"~s\" /e /is 1> nul", + [filename:nativename(Source), + filename:nativename(Dest)]); + false -> + ?FMT("robocopy \"~s\" \"~s\" \"~s\" /e /is 1> nul", + [filename:nativename(filename:dirname(Source)), + filename:nativename(Dest), + filename:basename(Source)]) + end, + Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), - case length(R) > 0 of - %% when xcopy fails, stdout is empty and and error message is printed - %% to stderr (which is redirected to nul) - true -> ok; - false -> - {error, lists:flatten( - io_lib:format("Failed to xcopy from ~s to ~s~n", - [Source, Dest]))} + case win32_ok(Res) of + true -> ok; + false -> + {error, lists:flatten( + io_lib:format("Failed to copy ~s to ~s~n", + [Source, Dest]))} end. cp_r_win32({true, SourceDir}, {true, DestDir}) -> %% from directory to directory - SourceBase = filename:basename(SourceDir), - ok = case file:make_dir(filename:join(DestDir, SourceBase)) of + ok = case file:make_dir(DestDir) of {error, eexist} -> ok; Other -> Other end, - ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase)); + ok = xcopy_win32(SourceDir, DestDir); cp_r_win32({false, Source} = S,{true, DestDir}) -> %% from file to directory cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))}); diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index 2d83579..e2f3f69 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -68,6 +68,7 @@ compare_url(Dir, Url) -> CurrentUrl1 = string:strip(string:strip(CurrentUrl, both, $\n), both, $\r), ParsedUrl = parse_git_url(Url), ParsedCurrentUrl = parse_git_url(CurrentUrl1), + ?DEBUG("Comparing git url ~p with ~p", [ParsedUrl, ParsedCurrentUrl]), ParsedCurrentUrl =:= ParsedUrl. parse_git_url("git@" ++ HostPath) -> @@ -76,6 +77,9 @@ parse_git_url("git@" ++ HostPath) -> parse_git_url("git://" ++ HostPath) -> [Host | Path] = string:tokens(HostPath, "/"), {Host, filename:rootname(filename:join(Path), ".git")}; +parse_git_url("http://" ++ HostPath) -> + [Host | Path] = string:tokens(HostPath, "/"), + {Host, filename:rootname(filename:join(Path), ".git")}; parse_git_url("https://" ++ HostPath) -> [Host | Path] = string:tokens(HostPath, "/"), {Host, filename:rootname(filename:join(Path), ".git")}. @@ -109,28 +113,23 @@ download(Dir, {git, Url, Rev}, _State) -> rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]). make_vsn(Dir) -> - {ok, Cwd} = file:get_cwd(), - try - ok = file:set_cwd(Dir), - {Vsn, RawRef, RawCount} = collect_default_refcount(), - {plain, build_vsn_string(Vsn, RawRef, RawCount)} - after - file:set_cwd(Cwd) - end. + {Vsn, RawRef, RawCount} = collect_default_refcount(Dir), + {plain, build_vsn_string(Vsn, RawRef, RawCount)}. %% Internal functions -collect_default_refcount() -> +collect_default_refcount(Dir) -> %% Get the tag timestamp and minimal ref from the system. The %% timestamp is really important from an ordering perspective. AbortMsg1 = "Getting log of git dependency failed in " ++ rebar_dir:get_cwd(), {ok, String} = rebar_utils:sh("git log -n 1 --pretty=format:\"%h\n\" ", [{use_stdout, false}, + {cd, Dir}, {debug_abort_on_error, AbortMsg1}]), RawRef = string:strip(String, both, $\n), - {Tag, TagVsn} = parse_tags(), + {Tag, TagVsn} = parse_tags(Dir), {ok, RawCount} = case Tag of undefined -> @@ -140,7 +139,7 @@ collect_default_refcount() -> {debug_abort_on_error, AbortMsg2}]), rebar_utils:line_count(PatchLines); _ -> - get_patch_count(Tag) + get_patch_count(Dir, Tag) end, {TagVsn, RawRef, RawCount}. @@ -158,8 +157,8 @@ build_vsn_string(Vsn, RawRef, Count) -> integer_to_list(Count), RefTag])) end. -get_patch_count(RawRef) -> - AbortMsg = "Getting rev-list of git dep failed in " ++ rebar_dir:get_cwd(), +get_patch_count(Dir, RawRef) -> + AbortMsg = "Getting rev-list of git dep failed in " ++ Dir, Ref = re:replace(RawRef, "\\s", "", [global]), Cmd = io_lib:format("git rev-list ~s..HEAD", [Ref]), @@ -169,10 +168,10 @@ get_patch_count(RawRef) -> rebar_utils:line_count(PatchLines). -parse_tags() -> +parse_tags(Dir) -> %% Don't abort on error, we want the bad return to be turned into 0.0.0 case rebar_utils:sh("git log --oneline --no-walk --tags --decorate", - [{use_stdout, false}, return_on_error]) of + [{use_stdout, false}, return_on_error, {cd, Dir}]) of {error, _} -> {undefined, "0.0.0"}; {ok, Line} -> diff --git a/src/rebar_hooks.erl b/src/rebar_hooks.erl index 56c4e91..857336c 100644 --- a/src/rebar_hooks.erl +++ b/src/rebar_hooks.erl @@ -14,12 +14,21 @@ run_all_hooks(Dir, Type, Command, Providers, State) -> run_hooks(Dir, Type, Command, State). run_provider_hooks(Dir, Type, Command, Providers, State) -> + case rebar_state:get(State, provider_hooks, []) of + [] -> + ok; + AllHooks -> + TypeHooks = proplists:get_value(Type, AllHooks, []), + run_provider_hooks(Dir, Type, Command, Providers, TypeHooks, State) + end. + +run_provider_hooks(_Dir, _Type, _Command, _Providers, [], _State) -> + ok; +run_provider_hooks(Dir, Type, Command, Providers, TypeHooks, State) -> PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps), code:add_pathsa(PluginDepsPaths), Providers1 = rebar_state:providers(State), State1 = rebar_state:providers(rebar_state:dir(State, Dir), Providers++Providers1), - AllHooks = rebar_state:get(State1, provider_hooks, []), - TypeHooks = proplists:get_value(Type, AllHooks, []), HookProviders = proplists:get_all_values(Command, TypeHooks), case rebar_core:do(HookProviders, State1) of @@ -58,24 +67,24 @@ format_error({bad_provider, Type, Command, Name}) -> %% ERL = ERLANG_ROOT_DIR/bin/erl %% ERLC = ERLANG_ROOT_DIR/bin/erl %% +run_hooks(Dir, pre, Command, State) -> + run_hooks(Dir, pre_hooks, Command, State); +run_hooks(Dir, post, Command, State) -> + run_hooks(Dir, post_hooks, Command, State); run_hooks(Dir, Type, Command, State) -> - Hooks = case Type of - pre -> - rebar_state:get(State, pre_hooks, []); - post -> - rebar_state:get(State, post_hooks, []); - _ -> - [] - end, - - Env = create_env(State), - lists:foreach(fun({_, C, _}=Hook) when C =:= Command -> - apply_hook(Dir, Env, Hook); - ({C, _}=Hook) when C =:= Command -> - apply_hook(Dir, Env, Hook); - (_) -> - continue - end, Hooks). + case rebar_state:get(State, Type, []) of + [] -> + ok; + Hooks -> + Env = create_env(State), + lists:foreach(fun({_, C, _}=Hook) when C =:= Command -> + apply_hook(Dir, Env, Hook); + ({C, _}=Hook) when C =:= Command -> + apply_hook(Dir, Env, Hook); + (_) -> + continue + end, Hooks) + end. apply_hook(Dir, Env, {Arch, Command, Hook}) -> case rebar_utils:is_arch(Arch) of diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 59ce0dc..450ff1e 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -95,7 +95,8 @@ make_vsn(_) -> request(Url, ETag) -> case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]}, [{relaxed, true}], - [{body_format, binary}]) of + [{body_format, binary}], + rebar) of {ok, {{_Version, 200, _Reason}, Headers, Body}} -> ?DEBUG("Successfully downloaded ~s", [Url]), {"etag", ETag1} = lists:keyfind("etag", 1, Headers), diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl index 7e12324..3e855de 100644 --- a/src/rebar_plugins.erl +++ b/src/rebar_plugins.erl @@ -63,7 +63,7 @@ handle_plugins(Profile, Plugins, State, Upgrade) -> handle_plugin(Profile, Plugin, State, Upgrade) -> try - {ok, Apps, State2} = rebar_prv_install_deps:handle_deps(Profile, State, [Plugin], Upgrade), + {Apps, State2} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State, [Plugin], Upgrade), {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 710922a..2b024cf 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -105,14 +105,18 @@ run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)). run_test_quiet(Opts) -> Pid = self(), + Ref = erlang:make_ref(), LogDir = proplists:get_value(logdir, Opts), - erlang:spawn_monitor(fun() -> + {_, Monitor} = erlang:spawn_monitor(fun() -> {ok, F} = file:open(filename:join([LogDir, "ct.latest.log"]), [write]), true = group_leader(F, self()), - Pid ! ct:run_test(Opts) + Pid ! {Ref, ct:run_test(Opts)} end), - receive Result -> handle_quiet_results(Opts, Result) end. + receive + {Ref, Result} -> handle_quiet_results(Opts, Result); + {'DOWN', Monitor, _, _, Reason} -> handle_results(?PRV_ERROR(Reason)) + end. handle_results(Results) when is_list(Results) -> Result = lists:foldl(fun sum_results/2, {0, 0, {0,0}}, Results), @@ -132,8 +136,6 @@ sum_results({Passed, Failed, {UserSkipped, AutoSkipped}}, handle_quiet_results(_, {error, _} = Result) -> handle_results(Result); -handle_quiet_results(_, {'DOWN', _, _, _, Reason}) -> - handle_results(?PRV_ERROR(Reason)); handle_quiet_results(CTOpts, Results) when is_list(Results) -> _ = [format_result(Result) || Result <- Results], case handle_results(Results) of @@ -345,23 +347,26 @@ reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest); reduce_path([], [".."|Rest]) -> reduce_path([], Rest); reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest). -remove_links(Path) -> - case ec_file:is_dir(Path) of - false -> ok; - true -> remove_links1(Path) - end. -remove_links1(Path) -> +remove_links(Path) -> + IsDir = ec_file:is_dir(Path), case ec_file:is_symlink(Path) of - true -> - file:delete(Path); - false -> - lists:foreach(fun(ChildPath) -> - remove_links(ChildPath) - end, sub_dirs(Path)) + true when IsDir -> + delete_dir_link(Path); + false when IsDir -> + lists:foreach(fun(ChildPath) -> + remove_links(ChildPath) + end, dir_entries(Path)); + _ -> file:delete(Path) end. -sub_dirs(Path) -> +delete_dir_link(Path) -> + case os:type() of + {unix, _} -> file:delete(Path); + {win32, _} -> file:del_dir(Path) + end. + +dir_entries(Path) -> {ok, SubDirs} = file:list_dir(Path), [filename:join(Path, SubDir) || SubDir <- SubDirs]. diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl index 6eb8a4f..25d5193 100644 --- a/src/rebar_prv_compile.erl +++ b/src/rebar_prv_compile.erl @@ -126,7 +126,20 @@ copy_app_dirs(State, OldAppDir, AppDir) -> false -> ok end, + filelib:ensure_dir(filename:join(AppDir, "dummy")), + + %% link or copy mibs if it exists + case filelib:is_dir(filename:join(OldAppDir, "mibs")) of + true -> + %% If mibs exist it means we must ensure priv exists. + %% mibs files are compiled to priv/mibs/ + filelib:ensure_dir(filename:join([OldAppDir, "priv", "dummy"])), + symlink_or_copy(OldAppDir, AppDir, "mibs"); + false -> + ok + end, + %% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref SrcDirs = rebar_dir:all_src_dirs(State, ["src"], ["test"]), [symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs]; diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 6c115b6..8c26521 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -337,16 +337,24 @@ write_coverdata(State, Task) -> ?WARN("Cover data export failed: ~p", [Reason]) end. -verbose(State) -> +command_line_opts(State) -> {Opts, _} = rebar_state:command_parsed_args(State), - case proplists:get_value(verbose, Opts, missing) of - missing -> rebar_state:get(State, cover_print_enabled, false); - Else -> Else + Opts. + +config_opts(State) -> + rebar_state:get(State, cover_opts, []). + +verbose(State) -> + Command = proplists:get_value(verbose, command_line_opts(State), undefined), + Config = proplists:get_value(verbose, config_opts(State), undefined), + case {Command, Config} of + {undefined, undefined} -> false; + {undefined, Verbose} -> Verbose; + {Verbose, _} -> Verbose end. cover_dir(State) -> - rebar_state:get(State, cover_data_dir, filename:join([rebar_dir:base_dir(State), - "cover"])). + filename:join([rebar_dir:base_dir(State), "cover"]). cover_opts(_State) -> [{reset, $r, "reset", boolean, help(reset)}, diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 15f1dac..1cf7b71 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -72,6 +72,7 @@ short_desc() -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> + maybe_fix_env(), ?INFO("Dialyzer starting, this may take a while...", []), code:add_pathsa(rebar_state:code_paths(State, all_deps)), Plt = get_plt(State), @@ -91,6 +92,13 @@ do(State) -> rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)) end. +%% This is used to workaround dialyzer quirk discussed here +%% https://github.com/rebar/rebar3/pull/489#issuecomment-107953541 +%% Dialyzer gets default plt location wrong way by peeking HOME environment +%% variable which usually is not defined on Windows. +maybe_fix_env() -> + os:putenv("DIALYZER_PLT", filename:join(rebar_dir:home_dir(), ".dialyzer_plt")). + -spec format_error(any()) -> iolist(). format_error({error_processing_apps, Error}) -> io_lib:format("Error in dialyzing apps: ~s", [Error]); @@ -380,7 +388,7 @@ run_dialyzer(State, Opts, Output) -> {check_plt, false} | Opts], ?DEBUG("Running dialyzer with options: ~p~n", [Opts2]), - _ = dialyzer:run(Opts2), + dialyzer:run(Opts2), {0, State} end. diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 768d41a..105bbb1 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -35,10 +35,7 @@ -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). --export([handle_deps/3, - handle_deps/4, - handle_deps/5, - +-export([handle_deps_as_profile/4, find_cycles/1, cull_compile/2]). @@ -76,8 +73,8 @@ do(State) -> Profiles = rebar_state:current_profiles(State), ProjectApps = rebar_state:project_apps(State), - {Apps, State1} = - lists:foldl(fun deps_per_profile/2, {[], State}, lists:reverse(Profiles)), + Upgrade = rebar_state:get(State, upgrade, false), + {Apps, State1} = deps_per_profile(Profiles, Upgrade, State), State2 = rebar_state:update_all_deps(State1, Apps), CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps], @@ -119,63 +116,77 @@ format_error({cycles, Cycles}) -> format_error(Reason) -> io_lib:format("~p", [Reason]). --spec handle_deps(atom(), rebar_state:t(), list()) -> - {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}. -handle_deps(Profile, State, Deps) -> - handle_deps(Profile, State, Deps, false, []). - --spec handle_deps(atom(), rebar_state:t(), list(), list() | boolean()) -> - {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}. -handle_deps(Profile, State, Deps, Upgrade) when is_boolean(Upgrade) -> - handle_deps(Profile, State, Deps, Upgrade, []); -handle_deps(Profile, State, Deps, Locks) when is_list(Locks) -> - Upgrade = rebar_state:get(State, upgrade, false), - handle_deps(Profile, State, Deps, Upgrade, Locks). - --spec handle_deps(atom(), rebar_state:t(), list(), boolean() | {true, binary(), integer()}, list()) - -> {ok, [rebar_app_info:t()], rebar_state:t()} | {error, string()}. -handle_deps(_Profile, State, [], _, _) -> - {ok, [], State}; -handle_deps(Profile, State0, Deps, Upgrade, Locks) -> - %% Split source deps from pkg deps, needed to keep backwards compatibility - DepsDir = profile_dep_dir(State0, Profile), - - {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State0, Locks, 0), - - %% Fetch transitive src deps - {State1, SrcApps, PkgDeps1, Seen} = update_src_deps(Profile, 0, SrcDeps, PkgDeps, [] - ,State0, Upgrade, sets:new(), Locks), - - {Solved, State4} = - case PkgDeps1 of - [] -> - {[], State1}; - _ -> - %% Read in package index and dep graph - {Packages, Graph} = rebar_state:packages(State1), - Registry = rebar_packages:registry(State1), - State2 = rebar_state:packages(rebar_state:registry(State1, Registry) - ,{Packages, Graph}), - - update_pkg_deps(Profile, Packages, PkgDeps1 - ,Graph, Upgrade, Seen, State2, Locks) - end, - - AllDeps = lists:ukeymerge(2 - ,lists:ukeysort(2, SrcApps) - ,lists:ukeysort(2, Solved)), - - {ok, AllDeps, State4}. +%% Allows other providers to install deps in a given profile +%% manually, outside of what is provided by rebar3's deps tuple. +handle_deps_as_profile(Profile, State, Deps, Upgrade) -> + Locks = [], + Level = 0, + DepsDir = profile_dep_dir(State, Profile), + + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level), + AllSrcProfileDeps = [{Profile, SrcDeps, Locks, Level}], + AllPkgProfileDeps = [{Profile, Locks, PkgDeps}], + {AllApps, PkgDeps1, Seen, State1} = handle_profile_level(AllSrcProfileDeps, AllPkgProfileDeps, Locks, sets:new(), Upgrade, State), + + handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, State1). + %% =================================================================== %% Internal functions %% =================================================================== -deps_per_profile(Profile, {Apps, State}) -> +%% finds all the deps in `{deps, ...}` for each profile provided. +deps_per_profile(Profiles, Upgrade, State) -> + Level = 0, + {AllProfileDeps, PkgDeps} = lists:foldl(fun(Profile, {SrcAcc, PkgAcc}) -> + {Src, Pkg} = parse_profile_deps(State, Profile, Level), + {[Src | SrcAcc], [Pkg | PkgAcc]} + end, {[], []}, Profiles), + {AllApps, PkgDeps1, Seen, State1} = handle_profile_level(AllProfileDeps, PkgDeps, [], sets:new(), Upgrade, State), + + handle_profile_pkg_level(PkgDeps1, AllApps, Seen, Upgrade, State1). + +parse_profile_deps(State, Profile, Level) -> + DepsDir = profile_dep_dir(State, Profile), Locks = rebar_state:get(State, {locks, Profile}, []), - ProfileDeps = rebar_state:get(State, {deps, Profile}, []), - {ok, NewApps, NewState} = handle_deps(Profile, State, ProfileDeps, Locks), - {NewApps++Apps, NewState}. + Deps = rebar_state:get(State, {deps, Profile}, []), + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, Level), + {{Profile, SrcDeps, Locks, Level}, {Profile, Locks, PkgDeps}}. + +%% Level-order traversal of all dependencies, across profiles. +%% If profiles x,y,z are present, then the traversal will go: +%% x0, y0, z0, x1, y1, z1, ..., xN, yN, zN. +handle_profile_level([], PkgDeps, SrcApps, Seen, _Upgrade, State) -> + {SrcApps, PkgDeps, Seen, State}; +handle_profile_level([{Profile, SrcDeps, Locks, Level} | Rest], PkgDeps, SrcApps, Seen, Upgrade, State) -> + {SrcDeps1, PkgDeps1, SrcApps1, State1, Seen1, Locks1} = + update_src_deps(Profile, Level, SrcDeps, [], SrcApps + ,State, Upgrade, Seen, Locks), + SrcDeps2 = case SrcDeps1 of + [] -> Rest; + _ -> Rest ++ [{Profile, SrcDeps1, Locks1, Level+1}] + end, + handle_profile_level(SrcDeps2, [{Profile, Locks1, PkgDeps1} | PkgDeps], SrcApps1++SrcApps, sets:union(Seen, Seen1), Upgrade, State1). + +handle_profile_pkg_level(PkgDeps, AllApps, Seen, Upgrade, State) -> + %% Read in package index and dep graph + {Packages, Graph} = rebar_state:packages(State), + Registry = rebar_packages:registry(State), + State1 = rebar_state:packages(rebar_state:registry(State, Registry) + ,{Packages, Graph}), + + lists:foldl(fun({_Profile, _, []}, {AllAcc, StateAcc}) -> + {AllAcc, StateAcc}; + ({Profile1, Locks, PkgDeps2}, {AllAcc, StateAcc}) -> + {Solved, StateAcc2} = update_pkg_deps(Profile1, Packages, PkgDeps2 + ,Graph, Upgrade, Seen, StateAcc, Locks), + + AllDeps = lists:ukeymerge(2 + ,lists:ukeysort(2, AllAcc) + ,lists:ukeysort(2, Solved)), + + {AllDeps, StateAcc2} + end, {AllApps, State1}, PkgDeps). find_cycles(Apps) -> case rebar_digraph:compile_order(Apps) of @@ -279,19 +290,15 @@ package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) -> -spec update_src_deps(atom(), non_neg_integer(), list(), list(), list(), rebar_state:t(), boolean(), sets:set(binary()), list()) -> {rebar_state:t(), list(), list(), sets:set(binary())}. update_src_deps(Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) -> - case lists:foldl( - fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, SeenAcc, LocksAcc}) -> - update_src_dep(AppInfo, Profile, Level, - SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, - Upgrade, SeenAcc, Locks, LocksAcc) - end, - {[], PkgDeps, SrcApps, State, Seen, Locks}, - rebar_utils:sort_deps(SrcDeps)) of - {[], NewPkgDeps, NewSrcApps, State1, Seen1, _NewLocks} -> - {State1, NewSrcApps, NewPkgDeps, Seen1}; - {NewSrcDeps, NewPkgDeps, NewSrcApps, State1, Seen1, NewLocks} -> - update_src_deps(Profile, Level+1, NewSrcDeps, NewPkgDeps, NewSrcApps, State1, Upgrade, Seen1, NewLocks) - end. + lists:foldl( + fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, SeenAcc, LocksAcc}) -> + update_src_dep(AppInfo, Profile, Level, + SrcDepsAcc, PkgDepsAcc, SrcAppsAcc, StateAcc, + Upgrade, SeenAcc, Locks, LocksAcc) + end, + {[], PkgDeps, SrcApps, State, Seen, Locks}, + rebar_utils:sort_deps(SrcDeps)). + update_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) -> %% If not seen, add to list of locks to write out @@ -314,7 +321,7 @@ profile_dep_dir(State, Profile) -> _ -> rebar_dir:deps_dir(State) end. -update_seen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) -> +update_seen_src_dep(AppInfo, _Profile, _Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, BaseLocks, Locks) -> Name = rebar_app_info:name(AppInfo), %% If seen from lock file or user requested an upgrade %% don't print warning about skipping @@ -323,44 +330,32 @@ update_seen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, U false when not Upgrade -> warn_skip_deps(AppInfo, State); true -> ok end, - %% scan for app children here if upgrading - case Upgrade of - false -> - {SrcDeps, PkgDeps, SrcApps, State, Seen, Locks}; - true -> - {NewSrcDeps, NewPkgDeps, NewSrcApps, NewState, NewLocks} - = handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State, Locks), - {NewSrcDeps, NewPkgDeps, NewSrcApps, NewState, Seen, NewLocks} - end. + {SrcDeps, PkgDeps, SrcApps, State, Seen, Locks}. update_unseen_src_dep(AppInfo, Profile, Level, SrcDeps, PkgDeps, SrcApps, State, Upgrade, Seen, Locks) -> {NewSeen, State1} = maybe_lock(Profile, AppInfo, Seen, State, Level), {NewSrcDeps, NewPkgDeps, NewSrcApps, State2, NewLocks} = case Upgrade of - true -> - handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State1, Locks); - _ -> - {_, AppInfo1} = maybe_fetch(AppInfo, Profile, false, Seen, State1), - handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State1, Locks) - end, + true -> + handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State1, Seen, Locks); + _ -> + {_, AppInfo1} = maybe_fetch(AppInfo, Profile, false, Seen, State1), + handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State1, Locks) + end, {NewSrcDeps, NewPkgDeps, NewSrcApps, State2, NewSeen, NewLocks}. -handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) -> +handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Seen, Locks) -> Name = rebar_app_info:name(AppInfo), case lists:keyfind(Name, 1, Locks) of false -> - case maybe_fetch(AppInfo, Profile, true, sets:new(), State) of - {true, AppInfo1} -> - handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, - Level, State, Locks); - {false, AppInfo1} -> - {[AppInfo1|SrcDeps], PkgDeps, SrcApps, State, Locks} - end; + {_, AppInfo1} = maybe_fetch(AppInfo, Profile, true, Seen, State), + handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State, Locks); _StillLocked -> - {[AppInfo|SrcDeps], PkgDeps, SrcApps, State, Locks} + handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, + Level, State, Locks) end. handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) -> @@ -414,41 +409,27 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) -> false -> case rebar_app_discover:find_app(AppDir, all) of false -> - case already_in_default(AppInfo, State) of - false -> - case fetch_app(AppInfo, AppDir, State) of - true -> - maybe_symlink_default(State, Profile, AppDir, AppInfo), - {true, update_app_info(AppDir, AppInfo)}; - Other -> - {Other, AppInfo} - end; - {true, FoundApp} -> - %% Preserve the state we created with overrides - AppState = rebar_app_info:state(AppInfo), - FoundApp1 = rebar_app_info:state(FoundApp, AppState), - symlink_dep(rebar_app_info:dir(FoundApp1), AppDir), - {true, FoundApp1} + case fetch_app(AppInfo, AppDir, State) of + true -> + maybe_symlink_default(State, Profile, AppDir, AppInfo), + {true, update_app_info(AppDir, AppInfo)}; + Other -> + {Other, AppInfo} end; {true, AppInfo1} -> %% Preserve the state we created with overrides AppState = rebar_app_info:state(AppInfo), AppInfo2 = rebar_app_info:state(AppInfo1, AppState), - maybe_symlink_default(State, Profile, AppDir, AppInfo2), case sets:is_element(rebar_app_info:name(AppInfo), Seen) of true -> {false, AppInfo2}; false -> + maybe_symlink_default(State, Profile, AppDir, AppInfo2), {maybe_upgrade(AppInfo, AppDir, Upgrade, State), AppInfo2} end end end. -already_in_default(AppInfo, State) -> - Name = ec_cnv:to_list(rebar_app_info:name(AppInfo)), - DefaultAppDir = filename:join([rebar_state:get(State, base_dir, []), "default", "lib", Name]), - rebar_app_discover:find_app(DefaultAppDir, all). - needs_symlinking(State, Profile) -> case {rebar_state:current_profiles(State), Profile} of {[default], default} -> @@ -504,6 +485,7 @@ parse_deps(DepsDir, Deps, State, Locks, Level) -> end, {[], []}, Deps). parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_list(Vsn) -> + %% Versioned Package dependency CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of {ok, _App} -> @@ -514,6 +496,7 @@ parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]} end; parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name) -> + %% Unversioned package dependency {PkgName, PkgVsn} = get_package(ec_cnv:to_binary(Name), State), CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl index 5ccd054..02c185f 100644 --- a/src/rebar_prv_plugins_upgrade.erl +++ b/src/rebar_prv_plugins_upgrade.erl @@ -62,10 +62,7 @@ upgrade(Plugin, State) -> ?PRV_ERROR({not_found, Plugin}); {ok, P, Profile} -> State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR), - {ok, Apps, _State2} = rebar_prv_install_deps:handle_deps(Profile - ,State1 - ,[P] - ,true), + {Apps, _State2} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State1, [P], true), {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), diff --git a/src/rebar_prv_release.erl b/src/rebar_prv_release.erl index dc58047..2cf9b23 100644 --- a/src/rebar_prv_release.erl +++ b/src/rebar_prv_release.erl @@ -32,34 +32,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - Options = rebar_state:command_args(State), - DepsDir = rebar_dir:deps_dir(State), - ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), - LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, - [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), - OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), - AllOptions = string:join(["release" | Options], " "), - Cwd = rebar_state:dir(State), - Providers = rebar_state:providers(State), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - try - case rebar_state:get(State, relx, []) of - [] -> - relx:main([{lib_dirs, LibDirs} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions); - Config -> - relx:main([{lib_dirs, LibDirs} - ,{config, lists:reverse(Config)} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions) - end, - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), - {ok, State} - catch - throw:T -> - {error, {rlx_prv_release, T}} - end. + rebar_relx:do(rlx_prv_release, "release", ?PROVIDER, State). -spec format_error(any()) -> iolist(). format_error(Reason) -> diff --git a/src/rebar_prv_relup.erl b/src/rebar_prv_relup.erl index aed12a6..a4cd8ae 100644 --- a/src/rebar_prv_relup.erl +++ b/src/rebar_prv_relup.erl @@ -33,34 +33,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - Options = rebar_state:command_args(State), - DepsDir = rebar_dir:deps_dir(State), - ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), - LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, - [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), - OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), - AllOptions = string:join(["relup" | Options], " "), - Cwd = rebar_state:dir(State), - Providers = rebar_state:providers(State), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - try - case rebar_state:get(State, relx, []) of - [] -> - relx:main([{lib_dirs, LibDirs} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions); - Config -> - relx:main([{lib_dirs, LibDirs} - ,{config, lists:reverse(Config)} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions) - end, - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), - {ok, State} - catch - throw:T -> - {error, {rlx_prv_release, T}} - end. + rebar_relx:do(rlx_prv_release, "relup", ?PROVIDER, State). -spec format_error(any()) -> iolist(). format_error(Reason) -> diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 84ad723..3c6369a 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -90,9 +90,10 @@ shell(State) -> %% their application masters never gets the new group leader (held in %% their internal state) maybe_boot_apps(State), - rebar_agent:start_link(State), - %% this call never returns (until user quits shell) - timer:sleep(infinity). + simulate_proc_lib(), + true = register(rebar_agent, self()), + {ok, GenState} = rebar_agent:init(State), + gen_server:enter_loop(rebar_agent, [], GenState, {local, rebar_agent}, hibernate). info() -> "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n". @@ -145,6 +146,11 @@ maybe_boot_apps(State) -> boot_apps(Apps) end. +simulate_proc_lib() -> + FakeParent = spawn_link(fun() -> timer:sleep(infinity) end), + put('$ancestors', [FakeParent]), + put('$initial_call', {rebar_agent, init, 1}). + setup_name(State) -> {Opts, _} = rebar_state:command_parsed_args(State), case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of diff --git a/src/rebar_prv_tar.erl b/src/rebar_prv_tar.erl index 17d7b0b..b3a12c0 100644 --- a/src/rebar_prv_tar.erl +++ b/src/rebar_prv_tar.erl @@ -32,29 +32,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> - Options = rebar_state:command_args(State), - DepsDir = rebar_dir:deps_dir(State), - ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), - LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, - [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), - OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), - AllOptions = string:join(["tar" | Options], " "), - Cwd = rebar_state:dir(State), - Providers = rebar_state:providers(State), - rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State), - case rebar_state:get(State, relx, []) of - [] -> - relx:main([{lib_dirs, LibDirs} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions); - Config -> - relx:main([{lib_dirs, LibDirs} - ,{config, lists:reverse(Config)} - ,{output_dir, OutputDir} - ,{caller, api}], AllOptions) - end, - rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State), - {ok, State}. + rebar_relx:do(rlx_prv_release, "tar", ?PROVIDER, State). -spec format_error(any()) -> iolist(). format_error(Reason) -> diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 6838bab..64fe65e 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -43,7 +43,8 @@ do(State) -> Url = rebar_state:get(State, rebar_packages_cdn, "https://s3.amazonaws.com/s3.hex.pm/registry.ets.gz"), {ok, _RequestId} = httpc:request(get, {Url, []}, - [], [{stream, TmpFile}, {sync, true}]), + [], [{stream, TmpFile}, {sync, true}], + rebar), {ok, Data} = file:read_file(TmpFile), Unzipped = zlib:gunzip(Data), ok = file:write_file(HexFile, Unzipped), diff --git a/src/rebar_relx.erl b/src/rebar_relx.erl new file mode 100644 index 0000000..a3adedd --- /dev/null +++ b/src/rebar_relx.erl @@ -0,0 +1,69 @@ +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 et + +-module(rebar_relx). + +-export([do/4, + format_error/1]). + +-include("rebar.hrl"). + +%% =================================================================== +%% Public API +%% =================================================================== + +-spec do(atom(), string(), atom(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(Module, Command, Provider, State) -> + Options = rebar_state:command_args(State), + DepsDir = rebar_dir:deps_dir(State), + ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS), + LibDirs = rebar_utils:filtermap(fun ec_file:exists/1, + [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]), + OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR), + AllOptions = string:join([Command | Options], " "), + Cwd = rebar_state:dir(State), + Providers = rebar_state:providers(State), + rebar_hooks:run_all_hooks(Cwd, pre, Provider, Providers, State), + try + case rebar_state:get(State, relx, []) of + [] -> + relx:main([{lib_dirs, LibDirs} + ,{output_dir, OutputDir} + ,{caller, api}], AllOptions); + Config -> + Config1 = update_config(Config), + relx:main([{lib_dirs, LibDirs} + ,{config, Config1} + ,{output_dir, OutputDir} + ,{caller, api}], AllOptions) + end, + rebar_hooks:run_all_hooks(Cwd, post, Provider, Providers, State), + {ok, State} + catch + throw:T -> + {error, {Module, T}} + end. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). + +%% To handle profiles rebar3 expects the provider to use the first entry +%% in a configuration key-value list as the value of a key if dups exist. +%% This does not work with relx. Some config options must not lose their +%% order (release which has an extends option is one). So here we pull out +%% options that are special so we can reverse the rest so what we expect +%% from a rebar3 profile is what we get on the relx side. +-define(SPECIAL_KEYS, [release, vm_args, sys_config, overlay_vars, lib_dirs]). + +update_config(Config) -> + {Special, Other} = + lists:foldl(fun(Tuple, {SpecialAcc, OtherAcc}) when is_tuple(Tuple) -> + case lists:member(element(1, Tuple), ?SPECIAL_KEYS) of + true -> + {[Tuple | SpecialAcc], OtherAcc}; + false -> + {SpecialAcc, [Tuple | OtherAcc]} + end + end, {[], []}, Config), + lists:reverse(Special) ++ Other. diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index cc59ed0..ebdf0fe 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -56,7 +56,8 @@ wordsize/0, tup_umerge/2, tup_sort/1, - line_count/1]). + line_count/1, + set_httpc_options/0]). %% for internal use only -export([otp_release/0]). @@ -127,11 +128,13 @@ sh_send(Command0, String, Options0) -> PortSettings = proplists:get_all_values(port_settings, Options) ++ [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], Port = open_port({spawn, Command}, PortSettings), - - %% allow us to send some data to the shell command's STDIN - %% Erlang doesn't let us get any reply after sending an EOF, though... - Port ! {self(), {command, String}}, - port_close(Port). + try + %% allow us to send some data to the shell command's STDIN + %% Erlang doesn't let us get any reply after sending an EOF, though... + Port ! {self(), {command, String}} + after + port_close(Port) + end. %% %% Options = [Option] -- defaults to [use_stdout, abort_on_error] @@ -154,15 +157,19 @@ sh(Command0, Options0) -> Command = lists:flatten(patch_on_windows(Command0, proplists:get_value(env, Options, []))), PortSettings = proplists:get_all_values(port_settings, Options) ++ - [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], + [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof], ?DEBUG("Port Cmd: ~s\nPort Opts: ~p\n", [Command, PortSettings]), Port = open_port({spawn, Command}, PortSettings), - case sh_loop(Port, OutputHandler, []) of - {ok, _Output} = Ok -> - Ok; - {error, {_Rc, _Output}=Err} -> - ErrorHandler(Command, Err) + try + case sh_loop(Port, OutputHandler, []) of + {ok, _Output} = Ok -> + Ok; + {error, {_Rc, _Output}=Err} -> + ErrorHandler(Command, Err) + end + after + port_close(Port) end. find_files(Dir, Regex) -> @@ -435,10 +442,14 @@ sh_loop(Port, Fun, Acc) -> sh_loop(Port, Fun, Fun(Line ++ "\n", Acc)); {Port, {data, {noeol, Line}}} -> sh_loop(Port, Fun, Fun(Line, Acc)); - {Port, {exit_status, 0}} -> - {ok, lists:flatten(lists:reverse(Acc))}; - {Port, {exit_status, Rc}} -> - {error, {Rc, lists:flatten(lists:reverse(Acc))}} + {Port, eof} -> + Data = lists:flatten(lists:reverse(Acc)), + receive + {Port, {exit_status, 0}} -> + {ok, Data}; + {Port, {exit_status, Rc}} -> + {error, {Rc, Data}} + end end. beam_to_mod(Dir, Filename) -> @@ -657,3 +668,19 @@ maybe_ends_in_comma(H) -> "," ++ Flag -> lists:reverse(Flag); _ -> false end. + +get_http_vars(Scheme) -> + GlobalConfigFile = rebar_dir:global_config(), + Config = rebar_config:consult_file(GlobalConfigFile), + proplists:get_value(Scheme, Config, []). + +set_httpc_options() -> + set_httpc_options(https_proxy, get_http_vars(https_proxy)), + set_httpc_options(proxy, get_http_vars(http_proxy)). + +set_httpc_options(_, []) -> + ok; + +set_httpc_options(Scheme, Proxy) -> + {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), + httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 8ee8af4..3f95e4f 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -20,7 +20,8 @@ checkout_priority/1, highest_version_of_pkg_dep/1, parse_transform_test/1, - erl_first_files_test/1]). + erl_first_files_test/1, + mib_test/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -48,7 +49,7 @@ all() -> recompile_when_opts_change, dont_recompile_when_opts_dont_change, dont_recompile_yrl_or_xrl, delete_beam_if_source_deleted, deps_in_path, checkout_priority, highest_version_of_pkg_dep, - parse_transform_test, erl_first_files_test]. + parse_transform_test, erl_first_files_test, mib_test]. build_basic_app(Config) -> AppDir = ?config(apps, Config), @@ -163,9 +164,10 @@ recompile_when_hrl_changes(Config) -> ModTime = [filelib:last_modified(filename:join([EbinDir, F])) || F <- Files, filename:extension(F) == ".beam"], + timer:sleep(1000), - os:cmd("touch " ++ HeaderFile), + rebar_file_utils:touch(HeaderFile), rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), @@ -274,9 +276,9 @@ delete_beam_if_source_deleted(Config) -> rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), - SrcDir = filename:join([AppDir, "_build", "default", "lib", Name, "src"]), + _SrcDir = filename:join([AppDir, "_build", "default", "lib", Name, "src"]), ?assert(filelib:is_regular(filename:join(EbinDir, "not_a_real_src_" ++ Name ++ ".beam"))), - file:delete(filename:join(SrcDir, "not_a_real_src_" ++ Name ++ ".erl")), + file:delete(filename:join([AppDir, "src", "not_a_real_src_" ++ Name ++ ".erl"])), rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}), @@ -307,6 +309,7 @@ deps_in_path(Config) -> ?assertEqual([], [Path || Path <- code:get_path(), {match, _} <- [re:run(Path, DepName)]]), %% Hope not to find pkg name in there + ?assertEqual([], [Path || Path <- code:get_path(), {match, _} <- [re:run(Path, PkgName)]]), %% Build things @@ -483,3 +486,48 @@ erl_first_files_test(Config) -> D = proplists:get_value(number, d:module_info(attributes)), E = proplists:get_value(number, e:module_info(attributes)), ?assertEqual([B,D,A,E], lists:sort([A,B,D,E])). + +mib_test(Config) -> + AppDir = ?config(apps, Config), + + RebarConfig = [], + + 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]), + + MibsSrc = <<"-- SIMPLE-MIB.\n" +"-- This is just a simple MIB used for testing!\n" +"--\n" +"SIMPLE-MIB DEFINITIONS ::= BEGIN\n" +"IMPORTS\n" +" MODULE-IDENTITY, enterprises\n" +" FROM SNMPv2-SMI;\n" +"\n" +"ericsson MODULE-IDENTITY\n" +" LAST-UPDATED\n" +" \"201403060000Z\"\n" +" ORGANIZATION\n" +" \"rebar\"\n" +" CONTACT-INFO\n" +" \"rebar <rebar@example.com>\n" +" or\n" +" whoever is currently responsible for the SIMPLE\n" +" enterprise MIB tree branch (enterprises.999).\"\n" +" DESCRIPTION\n" +" \"This very small module is made available\n" +" for mib-compilation testing.\"\n" +" ::= { enterprises 999 }\n" +"END\n">>, + + ok = filelib:ensure_dir(filename:join([AppDir, "mibs", "dummy"])), + ok = file:write_file(filename:join([AppDir, "mibs", "SIMPLE-MIB.mib"]), MibsSrc), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), + + %% check a beam corresponding to the src in the extra src_dir exists in ebin + PrivMibsDir = filename:join([AppDir, "_build", "default", "lib", Name, "priv", "mibs"]), + true = filelib:is_file(filename:join([PrivMibsDir, "SIMPLE-MIB.bin"])), + + %% check the extra src_dir was linked into the _build dir + true = filelib:is_dir(filename:join([AppDir, "_build", "default", "lib", Name, "mibs"])). diff --git a/test/rebar_cover_SUITE.erl b/test/rebar_cover_SUITE.erl index 0bead99..1fae92c 100644 --- a/test/rebar_cover_SUITE.erl +++ b/test/rebar_cover_SUITE.erl @@ -8,7 +8,6 @@ flag_coverdata_written/1, config_coverdata_written/1, index_written/1, - config_alt_coverdir/1, flag_verbose/1, config_verbose/1]). @@ -31,7 +30,6 @@ init_per_testcase(_, Config) -> all() -> [flag_coverdata_written, config_coverdata_written, index_written, - config_alt_coverdir, flag_verbose, config_verbose]. flag_coverdata_written(Config) -> @@ -79,23 +77,6 @@ index_written(Config) -> true = filelib:is_file(filename:join([AppDir, "_build", "test", "cover", "index.html"])). -config_alt_coverdir(Config) -> - AppDir = ?config(apps, Config), - - Name = rebar_test_utils:create_random_name("cover_"), - Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, [kernel, stdlib]), - - CoverDir = filename:join(["coverage", "goes", "here"]), - - RebarConfig = [{erl_opts, [{d, some_define}]}, {cover_data_dir, CoverDir}], - rebar_test_utils:run_and_check(Config, - RebarConfig, - ["do", "eunit", "--cover", ",", "cover"], - {ok, [{app, Name}]}), - - true = filelib:is_file(filename:join([CoverDir, "index.html"])). - flag_verbose(Config) -> AppDir = ?config(apps, Config), @@ -118,7 +99,7 @@ config_verbose(Config) -> 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_print_enabled, true}], + RebarConfig = [{erl_opts, [{d, some_define}]}, {cover_opts, [verbose]}], rebar_test_utils:run_and_check(Config, RebarConfig, ["do", "eunit", "--cover", ",", "cover"], diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl index afd487e..73c4980 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -3,7 +3,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -all() -> [sub_app_deps, newly_added_dep, {group, git}, {group, pkg}]. +all() -> [sub_app_deps, newly_added_dep, http_proxy_settings, https_proxy_settings, {group, git}, {group, pkg}]. groups() -> [{all, [], [flat, pick_highest_left, pick_highest_right, @@ -33,6 +33,47 @@ init_per_testcase(newly_added_dep, Config) -> rebar_test_utils:init_rebar_state(Config); init_per_testcase(sub_app_deps, Config) -> rebar_test_utils:init_rebar_state(Config); +init_per_testcase(http_proxy_settings, Config) -> + %% Create private rebar.config + Priv = ?config(priv_dir, Config), + GlobalDir = filename:join(Priv, "global"), + GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]), + GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]), + + meck:new(rebar_dir, [passthrough]), + meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end), + meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end), + + %% Insert proxy variables into config + rebar_test_utils:create_config(GlobalConfigDir, + [{http_proxy, "http://localhost:1234"} + ]), + rebar_test_utils:init_rebar_state(Config); +init_per_testcase(https_proxy_settings, Config) -> + SupportsHttpsProxy = case erlang:system_info(otp_release) of + "R16"++_ -> true; + "R"++_ -> false; + _ -> true % 17 and up don't have a "R" in the version + end, + if not SupportsHttpsProxy -> + {skip, https_proxy_unsupported_before_R16}; + SupportsHttpsProxy -> + %% Create private rebar.config + Priv = ?config(priv_dir, Config), + GlobalDir = filename:join(Priv, "global"), + GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]), + GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]), + + meck:new(rebar_dir, [passthrough]), + meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end), + meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end), + + %% Insert proxy variables into config + rebar_test_utils:create_config(GlobalConfigDir, + [{https_proxy, "http://localhost:1234"} + ]), + rebar_test_utils:init_rebar_state(Config) + end; init_per_testcase(Case, Config) -> {Deps, Warnings, Expect} = deps(Case), Expected = case Expect of @@ -45,6 +86,12 @@ init_per_testcase(Case, Config) -> {warnings, Warnings} | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))]. +end_per_testcase(https_proxy_settings, Config) -> + meck:unload(rebar_dir), + Config; +end_per_testcase(http_proxy_settings, Config) -> + meck:unload(rebar_dir), + Config; end_per_testcase(_, Config) -> meck:unload(), Config. @@ -223,6 +270,25 @@ newly_added_dep(Config) -> {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}). +http_proxy_settings(_Config) -> + %% Load config + rebar_utils:set_httpc_options(), + rebar3:init_config(), + + %% Assert variable is right + ?assertEqual({ok,{{"localhost", 1234}, []}}, + httpc:get_option(proxy, rebar)). + +https_proxy_settings(_Config) -> + %% Load config + rebar_utils:set_httpc_options(), + rebar3:init_config(), + + %% Assert variable is right + ?assertEqual({ok,{{"localhost", 1234}, []}}, + httpc:get_option(https_proxy, rebar)). + + run(Config) -> {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), rebar_test_utils:run_and_check( diff --git a/test/rebar_hooks_SUITE.erl b/test/rebar_hooks_SUITE.erl index ec5cc9a..85ca0e5 100644 --- a/test/rebar_hooks_SUITE.erl +++ b/test/rebar_hooks_SUITE.erl @@ -114,7 +114,7 @@ run_hooks_for_plugins(Config) -> rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), PluginName = rebar_test_utils:create_random_name("plugin1_"), - mock_git_resource:mock([{config, [{pre_hooks, [{compile, "touch randomfile"}]}]}]), + mock_git_resource:mock([{config, [{pre_hooks, [{compile, "echo whatsup > randomfile"}]}]}]), RConfFile = rebar_test_utils:create_config(AppDir, [{plugins, [ diff --git a/test/rebar_install_deps_SUITE.erl b/test/rebar_install_deps_SUITE.erl index dca6308..be42e68 100644 --- a/test/rebar_install_deps_SUITE.erl +++ b/test/rebar_install_deps_SUITE.erl @@ -10,7 +10,8 @@ groups() -> [{all, [], [flat, pick_highest_left, pick_highest_right, pick_smallest1, pick_smallest2, circular1, circular2, circular_skip, - fail_conflict, default_profile, nondefault_profile]}, + fail_conflict, default_profile, nondefault_profile, + nondefault_pick_highest]}, {git, [], [{group, all}]}, {pkg, [], [{group, all}]}]. @@ -125,7 +126,10 @@ deps(nondefault_profile) -> {[{"B", []}, {"C", []}], [], - {ok, ["B", "C"]}}. + {ok, ["B", "C"]}}; +deps(nondefault_pick_highest) -> + %% This is all handled in setup_project + {[],[],{ok,[]}}. setup_project(fail_conflict, Config0, Deps) -> DepsType = ?config(deps_type, Config0), @@ -164,6 +168,34 @@ setup_project(nondefault_profile, Config0, Deps) -> mock_pkg_resource:mock([{pkgdeps, rebar_test_utils:flat_pkgdeps(Deps)}]) end, [{rebarconfig, RebarConf} | Config]; +setup_project(nondefault_pick_highest, Config0, _) -> + DepsType = ?config(deps_type, Config0), + Config = rebar_test_utils:init_rebar_state( + Config0, + "nondefault_pick_highest_"++atom_to_list(DepsType)++"_" + ), + AppDir = ?config(apps, Config), + rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]), + DefaultDeps = rebar_test_utils:expand_deps(DepsType, [{"B", [{"C", "1", []}]}]), + ProfileDeps = rebar_test_utils:expand_deps(DepsType, [{"C", "2", []}]), + DefaultTop = rebar_test_utils:top_level_deps(DefaultDeps), + ProfileTop = rebar_test_utils:top_level_deps(ProfileDeps), + RebarConf = rebar_test_utils:create_config( + AppDir, + [{deps, DefaultTop}, + {profiles, [{nondef, [{deps, ProfileTop}]}]}] + ), + case DepsType of + git -> + mock_git_resource:mock( + [{deps, rebar_test_utils:flat_deps(DefaultDeps ++ ProfileDeps)}] + ); + pkg -> + mock_pkg_resource:mock( + [{pkgdeps, rebar_test_utils:flat_pkgdeps(DefaultDeps ++ ProfileDeps)}] + ) + end, + [{rebarconfig, RebarConf} | Config]; setup_project(Case, Config0, Deps) -> DepsType = ?config(deps_type, Config0), Config = rebar_test_utils:init_rebar_state( @@ -228,7 +260,7 @@ default_profile(Config) -> || {dep, App} <- Apps]. nondefault_profile(Config) -> - %% The dependencies here are saved directly to the + %% The dependencies here are saved directly to the {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), AppDir = ?config(apps, Config), {ok, AppLocks} = ?config(expect, Config), @@ -262,6 +294,21 @@ nondefault_profile(Config) -> file:read_file_info(filename:join([BuildDir, "default", "lib", App]))) || {dep, App} <- Apps]. +nondefault_pick_highest(Config) -> + {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), + %AppDir = ?config(apps, Config), + rebar_test_utils:run_and_check( + Config, RebarConfig, ["as", "nondef", "lock"], + {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "2"}], "nondef"} + ), + rebar_test_utils:run_and_check( + Config, RebarConfig, ["lock"], + {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "1"}, {lock, "C", "1"}], "default"} + ), + rebar_test_utils:run_and_check( + Config, RebarConfig, ["as", "nondef", "lock"], + {ok, [{dep, "B"}, {lock, "B"}, {dep, "C", "2"}], "nondef"} + ). run(Config) -> {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), @@ -294,4 +341,3 @@ in_warnings(pkg, Warns, NameRaw, VsnRaw) -> Vsn = iolist_to_binary(VsnRaw), 1 =< length([1 || {_, [AppName, AppVsn]} <- Warns, AppName =:= Name, AppVsn =:= Vsn]). - diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl index 95eb6f6..85bd6f0 100644 --- a/test/rebar_pkg_SUITE.erl +++ b/test/rebar_pkg_SUITE.erl @@ -61,7 +61,7 @@ init_per_testcase(good_disconnect=Name, Config0) -> copy_to_cache(Pkg, Config), meck:unload(httpc), meck:new(httpc, [passthrough, unsticky]), - meck:expect(httpc, request, fun(_, _, _, _) -> {error, econnrefused} end), + meck:expect(httpc, request, fun(_, _, _, _, _) -> {error, econnrefused} end), Config; init_per_testcase(bad_disconnect=Name, Config0) -> Pkg = {<<"goodpkg">>, <<"1.0.0">>}, @@ -71,7 +71,7 @@ init_per_testcase(bad_disconnect=Name, Config0) -> Config = mock_config(Name, Config1), meck:unload(httpc), meck:new(httpc, [passthrough, unsticky]), - meck:expect(httpc, request, fun(_, _, _, _) -> {error, econnrefused} end), + meck:expect(httpc, request, fun(_, _, _, _, _) -> {error, econnrefused} end), Config. end_per_testcase(_, Config) -> @@ -186,9 +186,9 @@ mock_config(Name, Config) -> {ok, PkgContents} = file:read_file(filename:join(?config(data_dir, Config), PkgFile)), meck:new(httpc, [passthrough, unsticky]), meck:expect(httpc, request, - fun(get, {_Url, _Opts}, _, _) when GoodCache -> + fun(get, {_Url, _Opts}, _, _, _) when GoodCache -> {ok, {{Vsn, 304, <<"Not Modified">>}, [{"etag", ?good_etag}], <<>>}}; - (get, {_Url, _Opts}, _, _) -> + (get, {_Url, _Opts}, _, _, _) -> {ok, {{Vsn, 200, <<"OK">>}, [{"etag", ?good_etag}], PkgContents}} end), [{cache_root, CacheRoot}, diff --git a/test/rebar_release_SUITE.erl b/test/rebar_release_SUITE.erl index 3809106..1ef0771 100644 --- a/test/rebar_release_SUITE.erl +++ b/test/rebar_release_SUITE.erl @@ -6,7 +6,8 @@ all() -> [release, dev_mode_release, profile_dev_mode_override_release, - tar]. + tar, + extend_release]. init_per_testcase(Case, Config0) -> Config = rebar_test_utils:init_rebar_state(Config0), @@ -90,3 +91,21 @@ tar(Config) -> ["tar"], {ok, [{release, list_to_atom(Name), Vsn, false}, {tar, Name, Vsn}]} ). + +%% Test that the order of release config args is not lost. If it is extend would fail. +extend_release(Config) -> + AppDir = ?config(apps, Config), + Name = ?config(name, Config), + Vsn = "1.0.0", + {ok, RebarConfig} = + file:consult(rebar_test_utils:create_config(AppDir, + [{relx, [{release, {list_to_atom(Name), Vsn}, + [list_to_atom(Name)]}, + {release, {extended, Vsn, {extend, list_to_atom(Name)}}, + []}, + {lib_dirs, [AppDir]}]}])), + rebar_test_utils:run_and_check( + Config, RebarConfig, + ["release", "-n", "extended"], + {ok, [{release, extended, Vsn, false}]} + ). diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index e5de0a6..4a13e03 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -56,7 +56,11 @@ run_and_check(Config, RebarConfig, Command, Expect) -> ?assertEqual({error, Reason}, Res); {ok, Expected} -> {ok, _} = Res, - check_results(AppDir, Expected), + check_results(AppDir, Expected, "*"), + Res; + {ok, Expected, ProfileRun} -> + {ok, _} = Res, + check_results(AppDir, Expected, ProfileRun), Res; return -> Res @@ -105,16 +109,22 @@ create_config(AppDir, Contents) -> %% @doc Util to create a random variation of a given name. create_random_name(Name) -> - random:seed(os:timestamp()), + random_seed(), Name ++ erlang:integer_to_list(random:uniform(1000000)). %% @doc Util to create a random variation of a given version. create_random_vsn() -> - random:seed(os:timestamp()), + random_seed(), lists:flatten([erlang:integer_to_list(random:uniform(100)), ".", erlang:integer_to_list(random:uniform(100)), ".", erlang:integer_to_list(random:uniform(100))]). +random_seed() -> + <<A:32, B:32, C:32>> = crypto:rand_bytes(12), + random:seed({A,B,C}). + + + expand_deps(_, []) -> []; expand_deps(git, [{Name, Deps} | Rest]) -> Dep = {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}}, @@ -158,9 +168,9 @@ top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) -> %%%%%%%%%%%%%%% %%% Helpers %%% %%%%%%%%%%%%%%% -check_results(AppDir, Expected) -> - BuildDirs = filelib:wildcard(filename:join([AppDir, "_build", "*", "lib"])), - PluginDirs = filelib:wildcard(filename:join([AppDir, "_build", "*", "plugins"])), +check_results(AppDir, Expected, ProfileRun) -> + BuildDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "lib"])), + PluginDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "plugins"])), GlobalPluginDirs = filelib:wildcard(filename:join([AppDir, "global", "plugins"])), CheckoutsDir = filename:join([AppDir, "_checkouts"]), LockFile = filename:join([AppDir, "rebar.lock"]), @@ -323,12 +333,12 @@ write_app_src_file(Dir, Name, Version, Deps) -> ok = ec_file:write_term(Filename, get_app_metadata(ec_cnv:to_list(Name), Version, Deps)). erl_src_file(Name) -> - io_lib:format("-module(~s).\n" + io_lib:format("-module('~s').\n" "-export([main/0]).\n" "main() -> ok.\n", [filename:basename(Name, ".erl")]). erl_eunitized_src_file(Name) -> - io_lib:format("-module(~s).\n" + io_lib:format("-module('~s').\n" "-export([main/0]).\n" "main() -> ok.\n" "-ifdef(TEST).\n" @@ -338,7 +348,7 @@ erl_eunitized_src_file(Name) -> erl_eunit_suite_file(Name) -> BaseName = filename:basename(Name, ".erl"), - io_lib:format("-module(~s_tests).\n" + io_lib:format("-module('~s_tests').\n" "-compile(export_all).\n" "-ifndef(some_define).\n" "-define(some_define, false).\n" diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl index 79cf29e..f2d4133 100644 --- a/test/rebar_upgrade_SUITE.erl +++ b/test/rebar_upgrade_SUITE.erl @@ -10,7 +10,8 @@ groups() -> pair_a, pair_b, pair_ab, pair_c, pair_all, triplet_a, triplet_b, triplet_c, tree_a, tree_b, tree_c, tree_c2, tree_ac, tree_all, - delete_d, promote, stable_lock, fwd_lock]}, + delete_d, promote, stable_lock, fwd_lock, + compile_upgrade_parity]}, {git, [], [{group, all}]}, {pkg, [], [{group, all}]}]. @@ -404,7 +405,20 @@ upgrades(fwd_lock) -> %% file to include the result post-upgrade, and then %% run a regular lock to see that the lock file is respected %% in deps. - {"any", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}}. + {"any", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}}; +upgrades(compile_upgrade_parity) -> + {[{"A", "1", [{"D",[{"J",[]}]}, + {"E",[{"I","1",[]}]}]}, + {"B", "1", [{"F",[]}, + {"G",[]}]}, + {"C", "1", [{"H",[]}, + {"I","2",[]}]} + ], + [], + [], + {"", [{"A","1"}, "D", "J", "E", {"I","1"}, + {"B","1"}, "F", "G", + {"C","1"}, "H"]}}. %% TODO: add a test that verifies that unlocking files and then %% running the upgrade code is enough to properly upgrade things. @@ -518,6 +532,22 @@ fwd_lock(Config) -> Config, NewRebarConfig, ["lock", App], Expectation ). +compile_upgrade_parity(Config) -> + AppDir = ?config(apps, Config), + apply(?config(mock, Config), []), + {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), + %% compiling and upgrading should generate the same lockfiles when + %% deps are identical + Lockfile = filename:join([AppDir, "rebar.lock"]), + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, []}), + {ok, CompileLockData1} = file:read_file(Lockfile), + rebar_test_utils:run_and_check(Config, RebarConfig, ["upgrade"], {ok, []}), + {ok, UpgradeLockData} = file:read_file(Lockfile), + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, []}), + {ok, CompileLockData2} = file:read_file(Lockfile), + ?assertEqual(CompileLockData1, CompileLockData2), + ?assertEqual(CompileLockData1, UpgradeLockData). + run(Config) -> apply(?config(mock, Config), []), {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index e9b32e2..f04ab63 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -21,7 +21,8 @@ task_with_flag_with_trailing_comma/1, task_with_flag_with_commas/1, task_with_multiple_flags/1, - special_task_do/1]). + special_task_do/1, + sh_does_not_miss_messages/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -29,7 +30,8 @@ all() -> - [{group, args_to_tasks}]. + [{group, args_to_tasks}, + sh_does_not_miss_messages]. groups() -> [{args_to_tasks, [], [empty_arglist, @@ -118,3 +120,14 @@ special_task_do(_Config) -> "do", "bar,", "baz"]). +sh_does_not_miss_messages(_Config) -> + Source = "~nmain(_) ->~n io:format(\"donotmissme\").~n", + file:write_file("do_not_miss_messages", io_lib:format(Source,[])), + {ok, "donotmissme"} = rebar_utils:sh("escript do_not_miss_messages", []), + AnyMessageRemained = + receive + What -> What + after 100 -> + false + end, + AnyMessageRemained = false. |