diff options
87 files changed, 1726 insertions, 725 deletions
@@ -1,6 +1,7 @@ _checkouts .rebar3 rebar3 +rebar3.cmd _build .depsolver_plt *.beam diff --git a/.travis.yml b/.travis.yml index ff4a252..b18dc76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,22 @@ -sudo: false language: erlang -install: 'true' -otp_release: -- 19.1 -- 18.0 -- 17.5 -- R16B03-1 -- R15B03 -before_script: "./bootstrap" -script: "./rebar3 ct" +matrix: + include: + - os: linux + otp_release: R16B03-1 + - os: linux + otp_release: 17.5 + - os: linux + otp_release: 18.3 + - os: linux + otp_release: 19.3 + - os: osx + language: generic +before_script: + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi + ## should eventually use a tap that has previous erlang versions here + ## as this only uses the latest erlang available via brew + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install erlang; fi +script: "./bootstrap && ./rebar3 ct" branches: only: - master @@ -23,7 +31,6 @@ deploy: on: repo: erlang/rebar3 tags: true - - provider: s3 access_key_id: AKIAJAPYAQEFYCYSNL7Q secret_access_key: @@ -16,7 +16,7 @@ main(_) -> ,{getopt, []} ,{cf, []} ,{erlware_commons, ["ec_dictionary.erl", "ec_vsn.erl"]} - ,{certifi, []}], + ,{certifi, ["certifi_pt.erl"]}], Deps = get_deps(), [fetch_and_compile(Dep, Deps) || Dep <- BaseDeps], @@ -24,7 +24,7 @@ main(_) -> bootstrap_rebar3(), %% Build rebar.app from rebar.app.src - {ok, App} = rebar_app_info:new(rebar, "3.3.5", filename:absname("_build/default/lib/rebar/")), + {ok, App} = rebar_app_info:new(rebar, "3.4.2", filename:absname("_build/default/lib/rebar/")), rebar_otp_app:compile(rebar_state:new(), App), %% Because we are compiling files that are loaded already we want to silence @@ -52,21 +52,7 @@ main(_) -> rebar3:run(["escriptize"]), %% Done with compile, can turn back on error logger - error_logger:tty(true), - - %% Finally, update executable perms for our script on *nix, - %% or write out script files on win32. - ec_file:copy("_build/default/bin/rebar3", "./rebar3"), - case os:type() of - {unix,_} -> - [] = os:cmd("chmod u+x rebar3"), - ok; - {win32,_} -> - write_windows_scripts(), - ok; - _ -> - ok - end. + error_logger:tty(true). default_registry_file() -> {ok, [[Home]]} = init:get_argument(home), @@ -305,14 +291,6 @@ reset_env() -> application:unload(rebar), application:load(rebar). -write_windows_scripts() -> - CmdScript= - "@echo off\r\n" - "setlocal\r\n" - "set rebarscript=%~f0\r\n" - "escript.exe \"%rebarscript:.cmd=%\" %*\r\n", - ok = file:write_file("rebar3.cmd", CmdScript). - get_deps() -> case file:consult("rebar.lock") of {ok, [[]]} -> diff --git a/manpages/rebar3.1 b/manpages/rebar3.1 index 450e006..5c42f21 100644 --- a/manpages/rebar3.1 +++ b/manpages/rebar3.1 @@ -197,7 +197,7 @@ Create new project from templates. \fBpath\fR [\fI--app\fR] [\fI--base\fR] [\fI--bin\fR] [\fI--ebin\fR] [\fI--lib\fR] [\fI--priv\fR] [\fI-s\fR|\fI--separator\fR] [\fI--src\fR] [\fI--rel\fR] Print paths to build dirs in current profile. .IP -\fI--app\fR: Comma seperated list of applications to return paths for. +\fI--app\fR: Comma separated list of applications to return paths for. .IP \fI--base\fR: Return the `base' path of the current profile. .IP diff --git a/priv/shell-completion/fish/rebar3.fish b/priv/shell-completion/fish/rebar3.fish index fd28c97..9cd2c82 100644 --- a/priv/shell-completion/fish/rebar3.fish +++ b/priv/shell-completion/fish/rebar3.fish @@ -147,7 +147,7 @@ complete -f -c 'rebar3' -n '__fish_rebar3_using_command new' -s f -l force -d "O complete -f -c 'rebar3' -n '__fish_rebar3_using_command new' -a help -d "Display all variables and arguments for each template" complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a paths -d "Print paths to build dirs in current profile." -complete -f -c 'rebar3' -n '__fish_rebar3_needs_command paths' -l app -d "Comma seperated list of applications to return paths for." +complete -f -c 'rebar3' -n '__fish_rebar3_needs_command paths' -l app -d "Comma separated list of applications to return paths for." complete -f -c 'rebar3' -n '__fish_rebar3_needs_command paths' -l base -d "Return the `base` path of the current profile." complete -f -c 'rebar3' -n '__fish_rebar3_needs_command paths' -l bin -d "Return the `bin` path of the current profile." complete -f -c 'rebar3' -n '__fish_rebar3_needs_command paths' -l ebin -d "Return all `ebin` paths of the current profile`s applications." diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3 index 0abc340..490a824 100644 --- a/priv/shell-completion/zsh/_rebar3 +++ b/priv/shell-completion/zsh/_rebar3 @@ -110,7 +110,7 @@ _rebar3 () { ;; (path) _arguments \ - '(--app)--app[Comma seperated list of applications to return paths for.]:apps' \ + '(--app)--app[Comma separated list of applications to return paths for.]:apps' \ '(--base)--base[Return the `base` path of the current profile.]' \ '(--bin)--bin[Return the `bin` path of the current profile.]' \ '(--ebin)--ebin[Return all `ebin` paths of the current profile`s applications.]' \ diff --git a/priv/templates/LICENSE b/priv/templates/LICENSE index 41588ab..59e1345 100644 --- a/priv/templates/LICENSE +++ b/priv/templates/LICENSE @@ -1,29 +1,191 @@ -Copyright (c) {{copyright_year}}, {{author_name}} <{{author_email}}>. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -* The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright {{copyright_year}}, {{author_name}} <{{author_email}}>. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/priv/templates/Makefile b/priv/templates/Makefile index d3c3767..5d490fd 100644 --- a/priv/templates/Makefile +++ b/priv/templates/Makefile @@ -6,9 +6,9 @@ BASEDIR := $(abspath $(CURDIR)/..) PROJECT ?= $(notdir $(BASEDIR)) PROJECT := $(strip $(PROJECT)) -ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s/erts-~s/include/\", [code:root_dir(), erlang:system_info(version)]).") -ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, include)]).") -ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, lib)]).") +ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)]).") +ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]).") +ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]).") C_SRC_DIR = $(CURDIR) C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so diff --git a/priv/templates/gitignore b/priv/templates/gitignore index 468614d..40ca652 100644 --- a/priv/templates/gitignore +++ b/priv/templates/gitignore @@ -14,4 +14,5 @@ erl_crash.dump logs _build .idea +*.iml rebar3.crashdump diff --git a/priv/templates/otp_app.app.src b/priv/templates/otp_app.app.src index c18f82c..d694d87 100644 --- a/priv/templates/otp_app.app.src +++ b/priv/templates/otp_app.app.src @@ -11,6 +11,6 @@ {modules, []}, {maintainers, []}, - {licenses, []}, + {licenses, ["Apache 2.0"]}, {links, []} ]}. diff --git a/priv/templates/otp_lib.app.src b/priv/templates/otp_lib.app.src index 5b98a51..aa31966 100644 --- a/priv/templates/otp_lib.app.src +++ b/priv/templates/otp_lib.app.src @@ -10,6 +10,6 @@ {modules, []}, {maintainers, []}, - {licenses, []}, + {licenses, ["Apache 2.0"]}, {links, []} ]}. diff --git a/rebar.config b/rebar.config index 1d853c4..2fe37ee 100644 --- a/rebar.config +++ b/rebar.config @@ -2,19 +2,24 @@ %% ex: ts=4 sw=4 ft=erlang et {deps, [{erlware_commons, "1.0.0"}, - {ssl_verify_fun, "1.1.1"}, - {certifi, "0.4.0"}, + {ssl_verify_fun, "1.1.2"}, + {certifi, "2.0.0"}, {providers, "1.6.0"}, {getopt, "0.8.2"}, {bbmustache, "1.3.0"}, - {relx, "3.22.2"}, + {relx, "3.23.1"}, {cf, "0.2.2"}, - {cth_readable, "1.2.3"}, + {cth_readable, "1.3.0"}, {eunit_formatters, "0.3.1"}]}. {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", escriptize, - "cp $REBAR_BUILD_DIR/bin/rebar3 ./rebar3 && chmod u+x rebar3"}]}. + "cp \"$REBAR_BUILD_DIR/bin/rebar3\" ./rebar3"}, + {"win32", + escriptize, + "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ rebar3* " + "/njs /njh /nfl /ndl & exit /b 0"} % silence things + ]}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. @@ -33,8 +38,8 @@ %% Profiles {profiles, [{test, [ - {deps, [{meck, "0.8.2"}]}, - {erl_opts, [debug_info]} + {deps, [{meck, "0.8.7"}]}, + {erl_opts, [debug_info, nowarn_export_all]} ] }, @@ -1,24 +1,24 @@ {"1.1.0", [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.3.0">>},0}, - {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"2.0.0">>},0}, {<<"cf">>,{pkg,<<"cf">>,<<"0.2.2">>},0}, - {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.3.0">>},0}, {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.0.0">>},0}, {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0}, {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}, {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.22.2">>},0}, - {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}. + {<<"relx">>,{pkg,<<"relx">>,<<"3.23.1">>},0}, + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.2">>},0}]}. [ {pkg_hash,[ {<<"bbmustache">>, <<"2010ADAE78830992A4C69680115ECD7D475DD03A72C076BBADDCCBF2D4B32035">>}, - {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>}, + {<<"certifi">>, <<"A0C0E475107135F76B8C1D5BC7EFB33CD3815CB3CF3DEA7AEFDD174DABEAD064">>}, {<<"cf">>, <<"7F2913FFF90ABCABD0F489896CFEB0B0674F6C8DF6C10B17A83175448029896C">>}, - {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>}, + {<<"cth_readable">>, <<"3F3B4C9CA1C96D5986557A033647A0D7072E25C241AE5EACD894D490EB656706">>}, {<<"erlware_commons">>, <<"087467DE5833C0BB5B3CCDD387F9E9C1FB816A75B7A709629BF24B5ED3246C51">>}, {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>}, {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>}, {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>}, - {<<"relx">>, <<"AEE2EF6E9AC6D21D6661133B7A0BE6E81424DE9CDCA0012FC008BC677297C469">>}, - {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]} + {<<"relx">>, <<"8AF4433934D9BB664E8282D2E45AC5DEAFF44859DDAABBE50CD7D885581CD24D">>}, + {<<"ssl_verify_fun">>, <<"01289CAD67B280B7F8F7E87117966995FAD19236367386BE2A9D7716E92CE7FF">>}]} ]. @@ -2,6 +2,7 @@ %%% calls from a shell. -module(r3). -export([do/1, do/2]). +-export(['$handle_undefined_function'/2]). %% @doc alias for `rebar_agent:do/1' -spec do(atom()) -> ok | {error, term()}. @@ -10,3 +11,7 @@ do(Command) -> rebar_agent:do(Command). %% @doc alias for `rebar_agent:do/2' -spec do(atom(), atom()) -> ok | {error, term()}. do(Namespace, Command) -> rebar_agent:do(Namespace, Command). + +%% @private defer to rebar_agent +'$handle_undefined_function'(Cmd, Args) -> + rebar_agent:'$handle_undefined_function'(Cmd, Args). diff --git a/src/rebar3.erl b/src/rebar3.erl index 56bf3e8..eec8968 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -176,18 +176,18 @@ init_config() -> Verbosity = log_level(), ok = rebar_log:init(command_line, Verbosity), - Config = rebar_config:consult(), + Config = rebar_config:consult_root(), Config1 = rebar_config:merge_locks(Config, rebar_config:consult_lock_file(?LOCK_FILE)), %% If $HOME/.config/rebar3/rebar.config exists load and use as global config GlobalConfigFile = rebar_dir:global_config(), State = case filelib:is_regular(GlobalConfigFile) of true -> - ?DEBUG("Load global config file ~s", [GlobalConfigFile]), + ?DEBUG("Load global config file ~ts", [GlobalConfigFile]), try state_from_global_config(Config1, GlobalConfigFile) catch _:_ -> - ?WARN("Global config ~s exists but can not be read. Ignoring global config values.", [GlobalConfigFile]), + ?WARN("Global config ~ts exists but can not be read. Ignoring global config values.", [GlobalConfigFile]), rebar_state:new(Config1) end; false -> @@ -270,7 +270,7 @@ log_level() -> -spec version() -> ok. version() -> {ok, Vsn} = application:get_key(rebar, vsn), - ?CONSOLE("rebar ~s on Erlang/OTP ~s Erts ~s", + ?CONSOLE("rebar ~ts on Erlang/OTP ~ts Erts ~ts", [Vsn, erlang:system_info(otp_release), erlang:system_info(version)]). %% @private set global flag based on getopt option boolean value @@ -311,11 +311,11 @@ handle_error({error, {Module, Reason}}) -> ?DEBUG("Uncaught error: ~p ~p", [Module, Reason]), ?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []); _ -> - ?ERROR("~s", [Module:format_error(Reason)]) + ?ERROR("~ts", [Module:format_error(Reason)]) end, erlang:halt(1); handle_error({error, Error}) when is_list(Error) -> - ?ERROR("~s", [Error]), + ?ERROR("~ts", [Error]), erlang:halt(1); handle_error(Error) -> %% Nothing should percolate up from rebar_core; @@ -344,7 +344,7 @@ start_and_load_apps(Caller) -> ensure_running(asn1, Caller), ensure_running(public_key, Caller), ensure_running(ssl, Caller), - inets:start(), + ensure_running(inets, Caller), inets:start(httpc, [{profile, rebar}]). %% @doc Make sure a required app is running, or display an error message diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index ed9e45d..b5dcfcf 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -2,6 +2,7 @@ %%% to statefully maintain loaded project state into a running VM. -module(rebar_agent). -export([start_link/1, do/1, do/2]). +-export(['$handle_undefined_function'/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). @@ -22,13 +23,24 @@ start_link(State) -> %% @doc runs a given command in the agent's context. -spec do(atom()) -> ok | {error, term()}. do(Command) when is_atom(Command) -> - gen_server:call(?MODULE, {cmd, Command}, infinity). + gen_server:call(?MODULE, {cmd, Command}, infinity); +do(Args) when is_list(Args) -> + gen_server:call(?MODULE, {cmd, default, do, Args}, infinity). %% @doc runs a given command in the agent's context, under a given %% namespace. -spec do(atom(), atom()) -> ok | {error, term()}. do(Namespace, Command) when is_atom(Namespace), is_atom(Command) -> - gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity). + gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity); +do(Namespace, Args) when is_atom(Namespace), is_list(Args) -> + gen_server:call(?MODULE, {cmd, Namespace, do, Args}, infinity). + +'$handle_undefined_function'(Cmd, [Namespace, Args]) -> + gen_server:call(?MODULE, {cmd, Namespace, Cmd, Args}, infinity); +'$handle_undefined_function'(Cmd, [Args]) -> + gen_server:call(?MODULE, {cmd, default, Cmd, Args}, infinity); +'$handle_undefined_function'(Cmd, []) -> + gen_server:call(?MODULE, {cmd, default, Cmd}, infinity). %%%%%%%%%%%%%%%%% %%% CALLBACKS %%% @@ -42,11 +54,15 @@ init(State) -> %% @private handle_call({cmd, Command}, _From, State=#state{state=RState, cwd=Cwd}) -> MidState = maybe_show_warning(State), - {Res, NewRState} = run(default, Command, RState, Cwd), + {Res, NewRState} = run(default, Command, "", RState, Cwd), {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), + {Res, NewRState} = run(Namespace, Command, "", RState, Cwd), + {reply, Res, MidState#state{state=NewRState}, hibernate}; +handle_call({cmd, Namespace, Command, Args}, _From, State = #state{state=RState, cwd=Cwd}) -> + MidState = maybe_show_warning(State), + {Res, NewRState} = run(Namespace, Command, Args, RState, Cwd), {reply, Res, MidState#state{state=NewRState}, hibernate}; handle_call(_Call, _From, State) -> {noreply, State}. @@ -72,13 +88,14 @@ terminate(_Reason, _State) -> %%%%%%%%%%%%%%% %% @private runs the actual command and maintains the state changes --spec run(atom(), atom(), rebar_state:t(), file:filename()) -> +-spec run(atom(), atom(), string(), rebar_state:t(), file:filename()) -> {ok, rebar_state:t()} | {{error, term()}, rebar_state:t()}. -run(Namespace, Command, RState, Cwd) -> +run(Namespace, Command, StrArgs, RState, Cwd) -> try case rebar_dir:get_cwd() of Cwd -> - Args = [atom_to_list(Namespace), atom_to_list(Command)], + PArgs = getopt:tokenize(StrArgs), + Args = [atom_to_list(Namespace), atom_to_list(Command)] ++ PArgs, CmdState0 = refresh_state(RState, Cwd), CmdState1 = rebar_state:set(CmdState0, task, atom_to_list(Command)), CmdState = rebar_state:set(CmdState1, caller, api), @@ -140,7 +157,7 @@ refresh_paths(RState) -> {ok, Mods} -> case {length(Mods), length(Mods -- Blacklist)} of {X,X} -> - ?DEBUG("reloading ~p from ~s", [Modules, Path]), + ?DEBUG("reloading ~p from ~ts", [Modules, Path]), code:replace_path(App, Path), reload_modules(Modules); {_,_} -> diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index fd55960..cdd183c 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -7,8 +7,10 @@ find_unbuilt_apps/1, find_apps/1, find_apps/2, + find_apps/3, find_app/2, - find_app/3]). + find_app/3, + find_app/4]). -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). @@ -20,7 +22,9 @@ do(State, LibDirs) -> BaseDir = rebar_state:dir(State), Dirs = [filename:join(BaseDir, LibDir) || LibDir <- LibDirs], - Apps = find_apps(Dirs, all), + RebarOpts = rebar_state:opts(State), + SrcDirs = rebar_dir:src_dirs(RebarOpts, ["src"]), + Apps = find_apps(Dirs, SrcDirs, all), ProjectDeps = rebar_state:deps_names(State), DepsDir = rebar_dir:deps_dir(State), CurrentProfiles = rebar_state:current_profiles(State), @@ -56,7 +60,7 @@ do(State, LibDirs) -> rebar_state:project_apps(StateAcc1 ,rebar_app_info:deps(AppInfo2, ProjectDeps1)); false -> - ?INFO("Ignoring ~s", [Name]), + ?INFO("Ignoring ~ts", [Name]), StateAcc end end, State1, SortedApps). @@ -93,11 +97,11 @@ merge_deps(AppInfo, State) -> %% the application they are defined at. If an umbrella structure is used and %% they are deifned at the top level they will instead run in the context of %% the State and at the top level, not as part of an application. - Default = reset_hooks(rebar_state:default(State)), + CurrentProfiles = rebar_state:current_profiles(State), + Default = reset_hooks(rebar_state:default(State), CurrentProfiles), {C, State1} = project_app_config(AppInfo, State), AppInfo0 = rebar_app_info:update_opts(AppInfo, Default, C), - CurrentProfiles = rebar_state:current_profiles(State1), Name = rebar_app_info:name(AppInfo0), %% We reset the opts here to default so no profiles are applied multiple times @@ -167,44 +171,72 @@ project_app_config(AppInfo, State) -> maybe_reset_hooks(Dir, Opts, State) -> case ec_file:real_dir_path(rebar_dir:root_dir(State)) of Dir -> - reset_hooks(Opts); + CurrentProfiles = rebar_state:current_profiles(State), + reset_hooks(Opts, CurrentProfiles); _ -> Opts end. %% @doc make the hooks empty for a given set of options --spec reset_hooks(Opts) -> Opts when Opts :: rebar_dict(). -reset_hooks(Opts) -> - lists:foldl(fun(Key, OptsAcc) -> - rebar_opts:set(OptsAcc, Key, []) - end, Opts, [post_hooks, pre_hooks, provider_hooks, artifacts]). - -%% @doc find the directories for all apps --spec all_app_dirs([file:name()]) -> [file:name()]. +-spec reset_hooks(Opts, Profiles) -> + Opts when + Opts :: rebar_dict(), + Profiles :: [atom()]. +reset_hooks(Opts, CurrentProfiles) -> + AllHooks = [post_hooks, pre_hooks, provider_hooks, artifacts], + Opts1 = lists:foldl(fun(Key, OptsAcc) -> + rebar_opts:set(OptsAcc, Key, []) + end, Opts, AllHooks), + Profiles = rebar_opts:get(Opts1, profiles, []), + Profiles1 = lists:map(fun({P, ProfileOpts}) -> + case lists:member(P, CurrentProfiles) of + true -> + {P, [X || X={Key, _} <- ProfileOpts, + not lists:member(Key, AllHooks)]}; + false -> + {P, ProfileOpts} + end + end, Profiles), + rebar_opts:set(Opts1, profiles, Profiles1). + +%% @private find the directories for all apps, while detecting their source dirs +%% Returns the app dir with the respective src_dirs for them, in that order, +%% for every app found. +-spec all_app_dirs([file:name()]) -> [{file:name(), [file:name()]}]. all_app_dirs(LibDirs) -> lists:flatmap(fun(LibDir) -> - app_dirs(LibDir) + SrcDirs = find_config_src(LibDir, ["src"]), + app_dirs(LibDir, SrcDirs) end, LibDirs). -%% @doc find the directories based on the library directories --spec app_dirs([file:name()]) -> [file:name()]. -app_dirs(LibDir) -> - Path1 = filename:join([LibDir, - "src", - "*.app.src"]), - - Path2 = filename:join([LibDir, - "src", - "*.app.src.script"]), - - Path3 = filename:join([LibDir, - "ebin", - "*.app"]), +%% @private find the directories for all apps based on their source dirs +%% Returns the app dir with the respective src_dirs for them, in that order, +%% for every app found. +-spec all_app_dirs([file:name()], [file:name()]) -> [{file:name(), [file:name()]}]. +all_app_dirs(LibDirs, SrcDirs) -> + lists:flatmap(fun(LibDir) -> app_dirs(LibDir, SrcDirs) end, LibDirs). + +%% @private find the directories based on the library directories. +%% Returns the app dir with the respective src_dirs for them, in that order, +%% for every app found. +%% +%% The function returns the src directories since they might have been +%% detected in a top-level loop and we want to skip further detection +%% starting now. +-spec app_dirs([file:name()], [file:name()]) -> [{file:name(), [file:name()]}]. +app_dirs(LibDir, SrcDirs) -> + Paths = lists:append([ + [filename:join([LibDir, SrcDir, "*.app.src"]), + filename:join([LibDir, SrcDir, "*.app.src.script"])] + || SrcDir <- SrcDirs + ]), + EbinPath = filename:join([LibDir, "ebin", "*.app"]), lists:usort(lists:foldl(fun(Path, Acc) -> - Files = filelib:wildcard(ec_cnv:to_list(Path)), - [app_dir(File) || File <- Files] ++ Acc - end, [], [Path1, Path2, Path3])). + Files = filelib:wildcard(rebar_utils:to_list(Path)), + [{app_dir(File), SrcDirs} + || File <- Files] ++ Acc + end, [], [EbinPath | Paths])). %% @doc find all apps that haven't been built in a list of directories -spec find_unbuilt_apps([file:filename_all()]) -> [rebar_app_info:t()]. @@ -222,16 +254,32 @@ find_apps(LibDirs) -> %% app info records. -spec find_apps([file:filename_all()], valid | invalid | all) -> [rebar_app_info:t()]. find_apps(LibDirs, Validate) -> - rebar_utils:filtermap(fun(AppDir) -> - find_app(AppDir, Validate) - end, all_app_dirs(LibDirs)). + rebar_utils:filtermap( + fun({AppDir, AppSrcDirs}) -> + find_app(rebar_app_info:new(), AppDir, AppSrcDirs, Validate) + end, + all_app_dirs(LibDirs) + ). + +%% @doc for each directory passed, with the configured source directories, +%% find all apps according to the validity rule passed in. +%% Returns all the related app info records. +-spec find_apps([file:filename_all()], [file:filename_all()], valid | invalid | all) -> [rebar_app_info:t()]. +find_apps(LibDirs, SrcDirs, Validate) -> + rebar_utils:filtermap( + fun({AppDir, AppSrcDirs}) -> + find_app(rebar_app_info:new(), AppDir, AppSrcDirs, Validate) + end, + all_app_dirs(LibDirs, SrcDirs) + ). %% @doc check that a given app in a directory is there, and whether it's %% valid or not based on the second argument. Returns the related %% app info record. -spec find_app(file:filename_all(), valid | invalid | all) -> {true, rebar_app_info:t()} | false. find_app(AppDir, Validate) -> - find_app(rebar_app_info:new(), AppDir, Validate). + SrcDirs = find_config_src(AppDir, ["src"]), + find_app(rebar_app_info:new(), AppDir, SrcDirs, Validate). %% @doc check that a given app in a directory is there, and whether it's %% valid or not based on the second argument. Returns the related @@ -239,9 +287,29 @@ find_app(AppDir, Validate) -> -spec find_app(rebar_app_info:t(), file:filename_all(), valid | invalid | all) -> {true, rebar_app_info:t()} | false. find_app(AppInfo, AppDir, Validate) -> + %% if no src dir is passed, figure it out from the app info, with a default + %% of src/ + AppOpts = rebar_app_info:opts(AppInfo), + SrcDirs = rebar_dir:src_dirs(AppOpts, ["src"]), + find_app(AppInfo, AppDir, SrcDirs, Validate). + +%% @doc check that a given app in a directory is there, and whether it's +%% valid or not based on the second argument. The third argument includes +%% the directories where source files can be located. Returns the related +%% app info record. +-spec find_app(rebar_app_info:t(), file:filename_all(), + [file:filename_all()], valid | invalid | all) -> + {true, rebar_app_info:t()} | false. +find_app(AppInfo, AppDir, SrcDirs, Validate) -> AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])), - AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])), - AppSrcScriptFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src.script"])), + AppSrcFile = lists:append( + [filelib:wildcard(filename:join([AppDir, SrcDir, "*.app.src"])) + || SrcDir <- SrcDirs] + ), + AppSrcScriptFile = lists:append( + [filelib:wildcard(filename:join([AppDir, SrcDir, "*.app.src.script"])) + || SrcDir <- SrcDirs] + ), try_handle_app_file(AppInfo, AppFile, AppDir, AppSrcFile, AppSrcScriptFile, Validate). %% @doc find the directory that an appfile has @@ -318,7 +386,7 @@ try_handle_app_file(AppInfo0, [File], AppDir, AppSrcFile, _, Validate) -> end catch throw:{error, {Module, Reason}} -> - ?DEBUG("Falling back to app.src file because .app failed: ~s", [Module:format_error(Reason)]), + ?DEBUG("Falling back to app.src file because .app failed: ~ts", [Module:format_error(Reason)]), try_handle_app_src_file(AppInfo0, File, AppDir, AppSrcFile, Validate) end; try_handle_app_file(_AppInfo, Other, _AppDir, _AppSrcFile, _, _Validate) -> @@ -358,3 +426,15 @@ enable(State, AppInfo) -> -spec to_atom(binary()) -> atom(). to_atom(Bin) -> list_to_atom(binary_to_list(Bin)). + +%% @private when looking for unknown apps, it's possible they have a +%% rebar.config file specifying non-standard src_dirs. Check for a +%% possible config file and extract src_dirs from it. +find_config_src(AppDir, Default) -> + case rebar_config:consult(AppDir) of + [] -> + Default; + Terms -> + %% TODO: handle profiles I guess, but we don't have that info + proplists:get_value(src_dirs, Terms, Default) + end. diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index fdaadb8..050ccc1 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -107,43 +107,43 @@ new() -> -spec new(atom() | binary() | string()) -> {ok, t()}. new(AppName) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName)}}. + {ok, #app_info_t{name=rebar_utils:to_binary(AppName)}}. %% @doc Build a new app info value with only the name and version set. -spec new(atom() | binary() | string(), binary() | string()) -> {ok, t()}. new(AppName, Vsn) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), original_vsn=Vsn}}. %% @doc build a complete version of the app info with all fields set. -spec new(atom() | binary() | string(), binary() | string(), file:name()) -> {ok, t()}. new(AppName, Vsn, Dir) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), original_vsn=Vsn, - dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir)}}. + dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir)}}. %% @doc build a complete version of the app info with all fields set. -spec new(atom() | binary() | string(), binary() | string(), file:name(), list()) -> {ok, t()}. new(AppName, Vsn, Dir, Deps) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), original_vsn=Vsn, - dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir), + dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir), deps=Deps}}. %% @doc build a complete version of the app info with all fields set. -spec new(atom() | binary(), atom() | binary() | string(), binary() | string(), file:name(), list()) -> {ok, t()}. new(Parent, AppName, Vsn, Dir, Deps) -> - {ok, #app_info_t{name=ec_cnv:to_binary(AppName), + {ok, #app_info_t{name=rebar_utils:to_binary(AppName), parent=Parent, original_vsn=Vsn, - dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir), + dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir), deps=Deps}}. %% @doc update the opts based on the contents of a config @@ -199,7 +199,7 @@ name(#app_info_t{name=Name}) -> %% @doc set the name of the app. -spec name(t(), atom() | binary() | string()) -> t(). name(AppInfo=#app_info_t{}, AppName) -> - AppInfo#app_info_t{name=ec_cnv:to_binary(AppName)}. + AppInfo#app_info_t{name=rebar_utils:to_binary(AppName)}. %% @doc get the dictionary of options for the app. -spec opts(t()) -> rebar_dict(). @@ -248,16 +248,15 @@ set(AppInfo=#app_info_t{opts=Opts}, Key, Value) -> %% @doc finds the .app.src file for an app, if any. -spec app_file_src(t()) -> file:filename_all() | undefined. -app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name}) -> - AppFileSrc = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src"]), - case filelib:is_file(AppFileSrc) of - true -> - AppFileSrc; - false -> - undefined +app_file_src(#app_info_t{app_file_src=undefined, dir=Dir, name=Name, opts=Opts}) -> + CandidatePaths = [filename:join([rebar_utils:to_list(Dir), Src, rebar_utils:to_list(Name)++".app.src"]) + || Src <- rebar_opts:get(Opts, src_dirs, ["src"])], + case lists:dropwhile(fun(Path) -> not filelib:is_file(Path) end, CandidatePaths) of + [] -> undefined; + [AppFileSrc|_] -> AppFileSrc end; app_file_src(#app_info_t{app_file_src=AppFileSrc}) -> - ec_cnv:to_list(AppFileSrc). + rebar_utils:to_list(AppFileSrc). %% @doc sets the .app.src file for an app. An app without such a file %% can explicitly be set with `undefined'. @@ -265,12 +264,12 @@ app_file_src(#app_info_t{app_file_src=AppFileSrc}) -> app_file_src(AppInfo=#app_info_t{}, undefined) -> AppInfo#app_info_t{app_file_src=undefined}; app_file_src(AppInfo=#app_info_t{}, AppFileSrc) -> - AppInfo#app_info_t{app_file_src=ec_cnv:to_list(AppFileSrc)}. + AppInfo#app_info_t{app_file_src=rebar_utils:to_list(AppFileSrc)}. %% @doc finds the .app.src.script file for an app, if any. -spec app_file_src_script(t()) -> file:filename_all() | undefined. app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Name}) -> - AppFileSrcScript = filename:join([ec_cnv:to_list(Dir), "src", ec_cnv:to_list(Name)++".app.src.script"]), + AppFileSrcScript = filename:join([rebar_utils:to_list(Dir), "src", rebar_utils:to_list(Name)++".app.src.script"]), case filelib:is_file(AppFileSrcScript) of true -> AppFileSrcScript; @@ -278,7 +277,7 @@ app_file_src_script(#app_info_t{app_file_src_script=undefined, dir=Dir, name=Nam undefined end; app_file_src_script(#app_info_t{app_file_src_script=AppFileSrcScript}) -> - ec_cnv:to_list(AppFileSrcScript). + rebar_utils:to_list(AppFileSrcScript). %% @doc sets the .app.src.script file for an app. An app without such a file %% can explicitly be set with `undefined'. @@ -286,12 +285,12 @@ app_file_src_script(#app_info_t{app_file_src_script=AppFileSrcScript}) -> app_file_src_script(AppInfo=#app_info_t{}, undefined) -> AppInfo#app_info_t{app_file_src_script=undefined}; app_file_src_script(AppInfo=#app_info_t{}, AppFileSrcScript) -> - AppInfo#app_info_t{app_file_src_script=ec_cnv:to_list(AppFileSrcScript)}. + AppInfo#app_info_t{app_file_src_script=rebar_utils:to_list(AppFileSrcScript)}. %% @doc finds the .app file for an app, if any. -spec app_file(t()) -> file:filename_all() | undefined. app_file(#app_info_t{app_file=undefined, out_dir=Dir, name=Name}) -> - AppFile = filename:join([ec_cnv:to_list(Dir), "ebin", ec_cnv:to_list(Name)++".app"]), + AppFile = filename:join([rebar_utils:to_list(Dir), "ebin", rebar_utils:to_list(Name)++".app"]), case filelib:is_file(AppFile) of true -> AppFile; @@ -318,7 +317,7 @@ app_details(AppInfo=#app_info_t{app_details=[]}) -> rebar_file_utils:try_consult(AppFile) catch throw:{error, {Module, Reason}} -> - ?DEBUG("Warning, falling back to .app.src because of: ~s", + ?DEBUG("Warning, falling back to .app.src because of: ~ts", [Module:format_error(Reason)]), rebar_file_utils:try_consult(app_file_src(AppInfo)) end @@ -405,10 +404,10 @@ dir(#app_info_t{dir=Dir}) -> %% @doc sets the directory that contains the app. -spec dir(t(), file:name()) -> t(). dir(AppInfo=#app_info_t{out_dir=undefined}, Dir) -> - AppInfo#app_info_t{dir=ec_cnv:to_list(Dir), - out_dir=ec_cnv:to_list(Dir)}; + AppInfo#app_info_t{dir=rebar_utils:to_list(Dir), + out_dir=rebar_utils:to_list(Dir)}; dir(AppInfo=#app_info_t{}, Dir) -> - AppInfo#app_info_t{dir=ec_cnv:to_list(Dir)}. + AppInfo#app_info_t{dir=rebar_utils:to_list(Dir)}. %% @doc returns the directory where build artifacts for the app %% should go @@ -420,17 +419,17 @@ out_dir(#app_info_t{out_dir=OutDir}) -> %% should go -spec out_dir(t(), file:name()) -> t(). out_dir(AppInfo=#app_info_t{}, OutDir) -> - AppInfo#app_info_t{out_dir=ec_cnv:to_list(OutDir)}. + AppInfo#app_info_t{out_dir=rebar_utils:to_list(OutDir)}. %% @doc gets the directory where ebin files for the app should go -spec ebin_dir(t()) -> file:name(). ebin_dir(#app_info_t{out_dir=OutDir}) -> - ec_cnv:to_list(filename:join(OutDir, "ebin")). + rebar_utils:to_list(filename:join(OutDir, "ebin")). %% @doc gets the directory where private files for the app should go -spec priv_dir(t()) -> file:name(). priv_dir(#app_info_t{out_dir=OutDir}) -> - ec_cnv:to_list(filename:join(OutDir, "priv")). + rebar_utils:to_list(filename:join(OutDir, "priv")). %% @doc returns whether the app is source app or a package app. -spec resource_type(t()) -> pkg | src. @@ -520,7 +519,7 @@ all(Dir, Context, [File|Artifacts]) -> FilePath = filename:join(Dir, rebar_templater:render(File, Context)), case filelib:is_regular(FilePath) of false -> - ?DEBUG("Missing artifact ~s", [FilePath]), + ?DEBUG("Missing artifact ~ts", [FilePath]), {false, File}; true -> all(Dir, Context, Artifacts) diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl index 7154999..1d7ef5b 100644 --- a/src/rebar_app_utils.erl +++ b/src/rebar_app_utils.erl @@ -34,6 +34,7 @@ validate_application_info/2, parse_deps/5, parse_deps/6, + expand_deps_sources/2, dep_to_app/7, format_error/1]). @@ -145,7 +146,7 @@ parse_dep(Dep, Parent, DepsDir, State, Locks, Level) -> Dep -> Dep end, - case lists:keyfind(ec_cnv:to_binary(Name), 1, Locks) of + case lists:keyfind(rebar_utils:to_binary(Name), 1, Locks) of false -> parse_dep(Parent, Dep, DepsDir, false, State); LockedDep -> @@ -167,18 +168,20 @@ parse_dep(Dep, Parent, DepsDir, State, Locks, Level) -> IsLock :: boolean(), State :: rebar_state:t(). parse_dep(Parent, {Name, Vsn, {pkg, PkgName}}, DepsDir, IsLock, State) -> - {PkgName1, PkgVsn} = {ec_cnv:to_binary(PkgName), ec_cnv:to_binary(Vsn)}, + {PkgName1, PkgVsn} = {rebar_utils:to_binary(PkgName), + rebar_utils:to_binary(Vsn)}, dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn, undefined}, IsLock, State); parse_dep(Parent, {Name, {pkg, PkgName}}, DepsDir, IsLock, State) -> %% Package dependency with different package name from app name - dep_to_app(Parent, DepsDir, Name, undefined, {pkg, ec_cnv:to_binary(PkgName), undefined, undefined}, IsLock, State); + dep_to_app(Parent, DepsDir, Name, undefined, {pkg, rebar_utils:to_binary(PkgName), undefined, undefined}, IsLock, State); parse_dep(Parent, {Name, Vsn}, DepsDir, IsLock, State) when is_list(Vsn); is_binary(Vsn) -> %% Versioned Package dependency - {PkgName, PkgVsn} = {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}, + {PkgName, PkgVsn} = {rebar_utils:to_binary(Name), + rebar_utils:to_binary(Vsn)}, dep_to_app(Parent, DepsDir, PkgName, PkgVsn, {pkg, PkgName, PkgVsn, undefined}, IsLock, State); parse_dep(Parent, Name, DepsDir, IsLock, State) when is_atom(Name); is_binary(Name) -> %% Unversioned package dependency - dep_to_app(Parent, DepsDir, ec_cnv:to_binary(Name), undefined, {pkg, ec_cnv:to_binary(Name), undefined, undefined}, IsLock, State); + dep_to_app(Parent, DepsDir, rebar_utils:to_binary(Name), undefined, {pkg, rebar_utils:to_binary(Name), undefined, undefined}, IsLock, State); parse_dep(Parent, {Name, Source}, DepsDir, IsLock, State) when is_tuple(Source) -> dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State); parse_dep(Parent, {Name, _Vsn, Source}, DepsDir, IsLock, State) when is_tuple(Source) -> @@ -212,12 +215,12 @@ parse_dep(_, Dep, _, _, _) -> IsLock :: boolean(), State :: rebar_state:t(). dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> - CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), + CheckoutsDir = rebar_utils:to_list(rebar_dir:checkouts_dir(State, Name)), AppInfo = case rebar_app_info:discover(CheckoutsDir) of {ok, App} -> rebar_app_info:source(rebar_app_info:is_checkout(App, true), checkout); not_found -> - Dir = ec_cnv:to_list(filename:join(DepsDir, Name)), + Dir = rebar_utils:to_list(filename:join(DepsDir, Name)), {ok, AppInfo0} = case rebar_app_info:discover(Dir) of {ok, App} -> @@ -225,7 +228,7 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> not_found -> rebar_app_info:new(Parent, Name, Vsn, Dir, []) end, - update_source(AppInfo0, Source, State) + rebar_app_info:source(AppInfo0, Source) end, C = rebar_config:consult(rebar_app_info:dir(AppInfo)), AppInfo1 = rebar_app_info:update_opts(AppInfo, rebar_app_info:opts(AppInfo), C), @@ -236,6 +239,13 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) -> AppInfo5 = rebar_app_info:profiles(AppInfo4, [default]), rebar_app_info:is_lock(AppInfo5, IsLock). +%% @doc Takes a given application app_info record along with the project. +%% If the app is a package, resolve and expand the package definition. +-spec expand_deps_sources(rebar_app_info:t(), rebar_state:t()) -> + rebar_app_info:t(). +expand_deps_sources(Dep, State) -> + update_source(Dep, rebar_app_info:source(Dep), State). + %% @doc sets the source for a given dependency or app along with metadata %% around version if required. -spec update_source(rebar_app_info:t(), Source, rebar_state:t()) -> @@ -275,7 +285,7 @@ fetch_checksum(PkgName, PkgVsn, Hash, State) -> rebar_packages:registry_checksum({pkg, PkgName, PkgVsn, Hash}, State) catch _:_ -> - ?INFO("Package ~s-~s not found. Fetching registry updates and trying again...", [PkgName, PkgVsn]), + ?INFO("Package ~ts-~ts not found. Fetching registry updates and trying again...", [PkgName, PkgVsn]), {ok, _} = rebar_prv_update:do(State), rebar_packages:registry_checksum({pkg, PkgName, PkgVsn, Hash}, State) end. @@ -283,7 +293,7 @@ fetch_checksum(PkgName, PkgVsn, Hash, State) -> %% @doc convert a given exception's payload into an io description. -spec format_error(any()) -> iolist(). format_error({missing_package, Package}) -> - io_lib:format("Package not found in registry: ~s", [Package]); + io_lib:format("Package not found in registry: ~ts", [Package]); format_error({parse_dep, Dep}) -> io_lib:format("Failed parsing dep ~p", [Dep]); format_error(Error) -> @@ -302,7 +312,7 @@ get_package(Dep, Vsn, State) -> {ok, HighestDepVsn} -> {Dep, HighestDepVsn}; none -> - throw(?PRV_ERROR({missing_package, ec_cnv:to_binary(Dep)})) + throw(?PRV_ERROR({missing_package, rebar_utils:to_binary(Dep)})) end. %% @private checks that all the beam files have been properly @@ -310,7 +320,7 @@ get_package(Dep, Vsn, State) -> -spec has_all_beams(file:filename_all(), [module()]) -> true | ?PRV_ERROR({missing_module, module()}). has_all_beams(EbinDir, [Module | ModuleList]) -> - BeamFile = filename:join([EbinDir, ec_cnv:to_list(Module) ++ ".beam"]), + BeamFile = filename:join([EbinDir, rebar_utils:to_list(Module) ++ ".beam"]), case filelib:is_file(BeamFile) of true -> has_all_beams(EbinDir, ModuleList); diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 480e49c..3f273f1 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -199,15 +199,15 @@ compile_each([], _Config, _CompileFn) -> compile_each([Source | Rest], Config, CompileFn) -> case CompileFn(Source, Config) of ok -> - ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]); + ?DEBUG("~tsCompiled ~ts", [rebar_utils:indent(1), filename:basename(Source)]); {ok, Warnings} -> report(Warnings), - ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]); + ?DEBUG("~tsCompiled ~ts", [rebar_utils:indent(1), filename:basename(Source)]); skipped -> - ?DEBUG("~sSkipped ~s", [rebar_utils:indent(1), filename:basename(Source)]); + ?DEBUG("~tsSkipped ~ts", [rebar_utils:indent(1), filename:basename(Source)]); Error -> NewSource = format_error_source(Source, Config), - ?ERROR("Compiling ~s failed", [NewSource]), + ?ERROR("Compiling ~ts failed", [NewSource]), maybe_report(Error), ?DEBUG("Compilation failed: ~p", [Error]), ?FAIL @@ -258,7 +258,7 @@ maybe_report(_) -> %% @private Outputs a bunch of strings, including a newline -spec report([string()]) -> ok. report(Messages) -> - lists:foreach(fun(Msg) -> io:format("~s~n", [Msg]) end, Messages). + lists:foreach(fun(Msg) -> io:format("~ts~n", [Msg]) end, Messages). %% private format compiler errors into proper outputtable strings -spec format_errors(_, Extra, [err_or_warn()]) -> [string()] when @@ -274,10 +274,10 @@ format_errors(_MainSource, Extra, Errors) -> Extra :: string(). format_error(Source, Extra, {{Line, Column}, Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), - ?FMT("~s:~w:~w: ~s~s~n", [Source, Line, Column, Extra, ErrorDesc]); + ?FMT("~ts:~w:~w: ~ts~ts~n", [Source, Line, Column, Extra, ErrorDesc]); format_error(Source, Extra, {Line, Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), - ?FMT("~s:~w: ~s~s~n", [Source, Line, Extra, ErrorDesc]); + ?FMT("~ts:~w: ~ts~ts~n", [Source, Line, Extra, ErrorDesc]); format_error(Source, Extra, {Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), - ?FMT("~s: ~s~s~n", [Source, Extra, ErrorDesc]). + ?FMT("~ts: ~ts~ts~n", [Source, Extra, ErrorDesc]). diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 97e27ab..7316d83 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -26,7 +26,7 @@ %% ------------------------------------------------------------------- -module(rebar_config). --export([consult/0 +-export([consult_root/0 ,consult/1 ,consult_app_file/1 ,consult_file/1 @@ -46,15 +46,15 @@ %% Public API %% =================================================================== -%% @doc reads the default config file. --spec consult() -> [any()]. -consult() -> +%% @doc reads the default config file at the top of a full project +-spec consult_root() -> [any()]. +consult_root() -> consult_file(config_file()). %% @doc reads the default config file in a given directory. -spec consult(file:name()) -> [any()]. consult(Dir) -> - consult_file(filename:join(Dir, config_file())). + consult_file(filename:join(Dir, ?DEFAULT_CONFIG_FILE)). %% @doc reads a given app file, including the `.script' variations, %% if any can be found. @@ -120,7 +120,7 @@ write_lock_file(LockFile, Locks) -> file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])); _ -> file:write_file(LockFile, - io_lib:format("{~p,~n~p}.~n[~n~s~n].~n", + io_lib:format("{~p,~n~p}.~n[~n~ts~n].~n", [?CONFIG_VERSION, NewLocks, format_attrs(Attrs)])) end. @@ -359,7 +359,7 @@ check_newly_added_({Name, Source}, LockedDeps) -> false end; check_newly_added_(Dep, LockedDeps) when is_atom(Dep) -> - Name = ec_cnv:to_binary(Dep), + Name = rebar_utils:to_binary(Dep), case lists:keyfind(Name, 1, LockedDeps) of false -> {true, Name}; @@ -368,8 +368,8 @@ check_newly_added_(Dep, LockedDeps) when is_atom(Dep) -> 0 -> {true, Name}; _ -> - ?WARN("Newly added dep ~s is locked at a lower level. " - "If you really want to unlock it, use 'rebar3 upgrade ~s'", + ?WARN("Newly added dep ~ts is locked at a lower level. " + "If you really want to unlock it, use 'rebar3 upgrade ~ts'", [Name, Name]), false end diff --git a/src/rebar_core.erl b/src/rebar_core.erl index 14c4906..3ef7a0d 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -119,11 +119,11 @@ process_command(State, Command) -> State2 = rebar_state:command_parsed_args(State1, Args), do(TargetProviders, State2); {error, {invalid_option, Option}} -> - {error, io_lib:format("Invalid option ~s on task ~p", [Option, Command])}; + {error, io_lib:format("Invalid option ~ts on task ~p", [Option, Command])}; {error, {invalid_option_arg, {Option, Arg}}} -> - {error, io_lib:format("Invalid argument ~s to option ~s", [Arg, Option])}; + {error, io_lib:format("Invalid argument ~ts to option ~ts", [Arg, Option])}; {error, {missing_option_arg, Option}} -> - {error, io_lib:format("Missing argument to option ~s", [Option])} + {error, io_lib:format("Missing argument to option ~ts", [Option])} end end end. @@ -173,6 +173,6 @@ do([ProviderName | Rest], State) -> %% @doc convert a given exception's payload into an io description. -spec format_error(any()) -> iolist(). format_error({bad_provider_namespace, {Namespace, Name}}) -> - io_lib:format("Undefined command ~s in namespace ~s", [Name, Namespace]); + io_lib:format("Undefined command ~ts in namespace ~ts", [Name, Namespace]); format_error({bad_provider_namespace, Name}) -> - io_lib:format("Undefined command ~s", [Name]). + io_lib:format("Undefined command ~ts", [Name]). diff --git a/src/rebar_dialyzer_format.erl b/src/rebar_dialyzer_format.erl index 1d234c3..7cf4e63 100644 --- a/src/rebar_dialyzer_format.erl +++ b/src/rebar_dialyzer_format.erl @@ -31,7 +31,7 @@ format_warnings(Opts, Warnings) -> format_warning_(_Opts, Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) -> try String = message_to_string(Msg), - {File, [lists:flatten(fmt("~!c~4w~!!: ~s", [Line, String])) | Acc]} + {File, [lists:flatten(fmt("~!c~4w~!!: ~ts", [Line, String])) | Acc]} catch Error:Reason -> ?DEBUG("Failed to pretty format warning: ~p:~p", @@ -47,11 +47,11 @@ format_warning_(Opts, Warning = {_Tag, {SrcFile, Line}, Msg}, {_LastFile, Acc}) Dir = filename:dirname(File), Root = filename:rootname(Base), Ext = filename:extension(Base), - Path = re:replace(Dir, "^.*/_build/", "_build/", [{return, list}]), - Base1 = fmt("~!_c~s~!!~!__~s", [Root, Ext]), - F = fmt("~!__~s", [filename:join(Path, Base1)]), + Path = re:replace(Dir, "^.*/_build/", "_build/", [{return, list}, unicode]), + Base1 = fmt("~!_c~ts~!!~!__~ts", [Root, Ext]), + F = fmt("~!__~ts", [filename:join(Path, Base1)]), String = message_to_string(Msg), - {SrcFile, [lists:flatten(fmt("~n~s~n~!c~4w~!!: ~s", [F, Line, String])) | Acc]} + {SrcFile, [lists:flatten(fmt("~n~ts~n~!c~4w~!!: ~ts", [F, Line, String])) | Acc]} catch Error:Reason -> ?DEBUG("Failed to pretty format warning: ~p:~p~n~p", @@ -72,53 +72,53 @@ fmt(Fmt, Args) -> %%----- Warnings for general discrepancies ---------------- message_to_string({apply, [Args, ArgNs, FailReason, SigArgs, SigRet, Contract]}) -> - fmt("~!^Fun application with arguments ~!!~s ", + fmt("~!^Fun application with arguments ~!!~ts ", [bad_arg(ArgNs, Args)]) ++ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract); message_to_string({app_call, [M, F, Args, Culprit, ExpectedType, FoundType]}) -> - fmt("~!^The call~!! ~s:~s~s ~!^requires that" - "~!! ~s ~!^is of type ~!g~s~!^ not ~!r~s", + fmt("~!^The call~!! ~ts:~ts~ts ~!^requires that" + "~!! ~ts ~!^is of type ~!g~ts~!^ not ~!r~ts", [M, F, Args, Culprit, ExpectedType, FoundType]); message_to_string({bin_construction, [Culprit, Size, Seg, Type]}) -> - fmt("~!^Binary construction will fail since the ~!b~s~!^ field~!!" - " ~s~!^ in segment~!! ~s~!^ has type~!! ~s", + fmt("~!^Binary construction will fail since the ~!b~ts~!^ field~!!" + " ~ts~!^ in segment~!! ~ts~!^ has type~!! ~ts", [Culprit, Size, Seg, Type]); message_to_string({call, [M, F, Args, ArgNs, FailReason, SigArgs, SigRet, Contract]}) -> - fmt("~!^The call~!! ~w:~w~s ", [M, F, bad_arg(ArgNs, Args)]) ++ + fmt("~!^The call~!! ~w:~w~ts ", [M, F, bad_arg(ArgNs, Args)]) ++ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract); message_to_string({call_to_missing, [M, F, A]}) -> fmt("~!^Call to missing or unexported function ~!!~w:~w/~w", [M, F, A]); message_to_string({exact_eq, [Type1, Op, Type2]}) -> - fmt("~!^The test ~!!~s ~s ~s~!^ can never evaluate to 'true'", + fmt("~!^The test ~!!~ts ~ts ~ts~!^ can never evaluate to 'true'", [Type1, Op, Type2]); message_to_string({fun_app_args, [Args, Type]}) -> - fmt("~!^Fun application with arguments ~!!~s~!^ will fail" - " since the function has type ~!!~s", [Args, Type]); + fmt("~!^Fun application with arguments ~!!~ts~!^ will fail" + " since the function has type ~!!~ts", [Args, Type]); message_to_string({fun_app_no_fun, [Op, Type, Arity]}) -> - fmt("~!^Fun application will fail since ~!!~s ~!^::~!! ~s" + fmt("~!^Fun application will fail since ~!!~ts ~!^::~!! ~ts" " is not a function of arity ~!!~w", [Op, Type, Arity]); message_to_string({guard_fail, []}) -> "~!^Clause guard cannot succeed.~!!"; message_to_string({guard_fail, [Arg1, Infix, Arg2]}) -> - fmt("~!^Guard test ~!!~s ~s ~s~!^ can never succeed", + fmt("~!^Guard test ~!!~ts ~ts ~ts~!^ can never succeed", [Arg1, Infix, Arg2]); message_to_string({neg_guard_fail, [Arg1, Infix, Arg2]}) -> - fmt("~!^Guard test not(~!!~s ~s ~s~!^) can never succeed", + fmt("~!^Guard test not(~!!~ts ~ts ~ts~!^) can never succeed", [Arg1, Infix, Arg2]); message_to_string({guard_fail, [Guard, Args]}) -> - fmt("~!^Guard test ~!!~w~s~!^ can never succeed", + fmt("~!^Guard test ~!!~w~ts~!^ can never succeed", [Guard, Args]); message_to_string({neg_guard_fail, [Guard, Args]}) -> - fmt("~!^Guard test not(~!!~w~s~!^) can never succeed", + fmt("~!^Guard test not(~!!~w~ts~!^) can never succeed", [Guard, Args]); message_to_string({guard_fail_pat, [Pat, Type]}) -> - fmt("~!^Clause guard cannot succeed. The ~!!~s~!^ was matched" - " against the type ~!!~s", [Pat, Type]); + fmt("~!^Clause guard cannot succeed. The ~!!~ts~!^ was matched" + " against the type ~!!~ts", [Pat, Type]); message_to_string({improper_list_constr, [TlType]}) -> fmt("~!^Cons will produce an improper list" - " since its ~!b2~!!nd~!^ argument is~!! ~s", [TlType]); + " since its ~!b2~!!nd~!^ argument is~!! ~ts", [TlType]); message_to_string({no_return, [Type|Name]}) -> NameString = case Name of @@ -126,59 +126,59 @@ message_to_string({no_return, [Type|Name]}) -> [F, A] -> fmt("~!^Function ~!r~w/~w ", [F, A]) end, case Type of - no_match -> fmt("~s~!^has no clauses that will ever match",[NameString]); - only_explicit -> fmt("~s~!^only terminates with explicit exception", [NameString]); - only_normal -> fmt("~s~!^has no local return", [NameString]); - both -> fmt("~s~!^has no local return", [NameString]) + no_match -> fmt("~ts~!^has no clauses that will ever match",[NameString]); + only_explicit -> fmt("~ts~!^only terminates with explicit exception", [NameString]); + only_normal -> fmt("~ts~!^has no local return", [NameString]); + both -> fmt("~ts~!^has no local return", [NameString]) end; message_to_string({record_constr, [RecConstr, FieldDiffs]}) -> - fmt("~!^Record construction ~!!~s~!^ violates the" - " declared type of field ~!!~s", [RecConstr, FieldDiffs]); + fmt("~!^Record construction ~!!~ts~!^ violates the" + " declared type of field ~!!~ts", [RecConstr, FieldDiffs]); message_to_string({record_constr, [Name, Field, Type]}) -> fmt("~!^Record construction violates the declared type for ~!!#~w{}~!^" - " since ~!!~s~!^ cannot be of type ~!!~s", + " since ~!!~ts~!^ cannot be of type ~!!~ts", [Name, Field, Type]); message_to_string({record_matching, [String, Name]}) -> - fmt("~!^The ~!!~s~!^ violates the" + fmt("~!^The ~!!~ts~!^ violates the" " declared type for ~!!#~w{}", [String, Name]); message_to_string({record_match, [Pat, Type]}) -> - fmt("~!^Matching of ~!!~s~!^ tagged with a record name violates the" - " declared type of ~!!~s", [Pat, Type]); + fmt("~!^Matching of ~!!~ts~!^ tagged with a record name violates the" + " declared type of ~!!~ts", [Pat, Type]); message_to_string({pattern_match, [Pat, Type]}) -> - fmt("~!^The ~s~!^ can never match the type ~!g~s", + fmt("~!^The ~ts~!^ can never match the type ~!g~ts", [bad_pat(Pat), Type]); message_to_string({pattern_match_cov, [Pat, Type]}) -> - fmt("~!^The ~s~!^ can never match since previous" - " clauses completely covered the type ~!g~s", + fmt("~!^The ~ts~!^ can never match since previous" + " clauses completely covered the type ~!g~ts", [bad_pat(Pat), Type]); message_to_string({unmatched_return, [Type]}) -> - fmt("~!^Expression produces a value of type ~!!~s~!^," + fmt("~!^Expression produces a value of type ~!!~ts~!^," " but this value is unmatched", [Type]); message_to_string({unused_fun, [F, A]}) -> fmt("~!^Function ~!r~w/~w~!!~!^ will never be called", [F, A]); %%----- Warnings for specs and contracts ------------------- message_to_string({contract_diff, [M, F, _A, Contract, Sig]}) -> - fmt("~!^Type specification ~!!~w:~w~s~!^" - " is not equal to the success typing: ~!!~w:~w~s", + fmt("~!^Type specification ~!!~w:~w~ts~!^" + " is not equal to the success typing: ~!!~w:~w~ts", [M, F, Contract, M, F, Sig]); message_to_string({contract_subtype, [M, F, _A, Contract, Sig]}) -> - fmt("~!^Type specification ~!!~w:~w~s~!^" - " is a subtype of the success typing: ~!!~w:~w~s", + fmt("~!^Type specification ~!!~w:~w~ts~!^" + " is a subtype of the success typing: ~!!~w:~w~ts", [M, F, Contract, M, F, Sig]); message_to_string({contract_supertype, [M, F, _A, Contract, Sig]}) -> - fmt("~!^Type specification ~!!~w:~w~s~!^" - " is a supertype of the success typing: ~!!~w:~w~s", + fmt("~!^Type specification ~!!~w:~w~ts~!^" + " is a supertype of the success typing: ~!!~w:~w~ts", [M, F, Contract, M, F, Sig]); message_to_string({contract_range, [Contract, M, F, ArgStrings, Line, CRet]}) -> - fmt("~!^The contract ~!!~w:~w~s~!^ cannot be right because the" - " inferred return for ~!!~w~s~!^ on line ~!!~w~!^ is ~!!~s", + fmt("~!^The contract ~!!~w:~w~ts~!^ cannot be right because the" + " inferred return for ~!!~w~ts~!^ on line ~!!~w~!^ is ~!!~ts", [M, F, Contract, F, ArgStrings, Line, CRet]); message_to_string({invalid_contract, [M, F, A, Sig]}) -> fmt("~!^Invalid type specification for function~!! ~w:~w/~w." - "~!^ The success typing is~!! ~s", [M, F, A, Sig]); + "~!^ The success typing is~!! ~ts", [M, F, A, Sig]); message_to_string({extra_range, [M, F, A, ExtraRanges, SigRange]}) -> fmt("~!^The specification for ~!!~w:~w/~w~!^ states that the function" - " might also return ~!!~s~!^ but the inferred return is ~!!~s", + " might also return ~!!~ts~!^ but the inferred return is ~!!~ts", [M, F, A, ExtraRanges, SigRange]); message_to_string({overlapping_contract, [M, F, A]}) -> fmt("~!^Overloaded contract for ~!!~w:~w/~w~!^ has overlapping" @@ -189,62 +189,62 @@ message_to_string({spec_missing_fun, [M, F, A]}) -> [M, F, A]); %%----- Warnings for opaque type violations ------------------- message_to_string({call_with_opaque, [M, F, Args, ArgNs, ExpArgs]}) -> - fmt("~!^The call ~!!~w:~w~s~!^ contains ~!!~s~!^ when ~!!~s", + fmt("~!^The call ~!!~w:~w~ts~!^ contains ~!!~ts~!^ when ~!!~ts", [M, F, bad_arg(ArgNs, Args), form_positions(ArgNs), form_expected(ExpArgs)]); message_to_string({call_without_opaque, [M, F, Args, [{N,_,_}|_] = ExpectedTriples]}) -> - fmt("~!^The call ~!!~w:~w~s ~!^does not have~!! ~s", + fmt("~!^The call ~!!~w:~w~ts ~!^does not have~!! ~ts", [M, F, bad_arg(N, Args), form_expected_without_opaque(ExpectedTriples)]); message_to_string({opaque_eq, [Type, _Op, OpaqueType]}) -> - fmt("~!^Attempt to test for equality between a term of type ~!!~s~!^" - " and a term of opaque type ~!!~s", [Type, OpaqueType]); + fmt("~!^Attempt to test for equality between a term of type ~!!~ts~!^" + " and a term of opaque type ~!!~ts", [Type, OpaqueType]); message_to_string({opaque_guard, [Arg1, Infix, Arg2, ArgNs]}) -> - fmt("~!^Guard test ~!!~s ~s ~s~!^ contains ~!!~s", + fmt("~!^Guard test ~!!~ts ~ts ~ts~!^ contains ~!!~ts", [Arg1, Infix, Arg2, form_positions(ArgNs)]); message_to_string({opaque_guard, [Guard, Args]}) -> - fmt("~!^Guard test ~!!~w~s~!^ breaks the opaqueness of its" + fmt("~!^Guard test ~!!~w~ts~!^ breaks the opaqueness of its" " argument", [Guard, Args]); message_to_string({opaque_match, [Pat, OpaqueType, OpaqueTerm]}) -> Term = if OpaqueType =:= OpaqueTerm -> "the term"; true -> OpaqueTerm end, - fmt("~!^The attempt to match a term of type ~!!~s~!^ against the" - "~!! ~s~!^ breaks the opaqueness of ~!!~s", + fmt("~!^The attempt to match a term of type ~!!~ts~!^ against the" + "~!! ~ts~!^ breaks the opaqueness of ~!!~ts", [OpaqueType, Pat, Term]); message_to_string({opaque_neq, [Type, _Op, OpaqueType]}) -> - fmt("~!^Attempt to test for inequality between a term of type ~!!~s" - "~!^ and a term of opaque type ~!!~s", [Type, OpaqueType]); + fmt("~!^Attempt to test for inequality between a term of type ~!!~ts" + "~!^ and a term of opaque type ~!!~ts", [Type, OpaqueType]); message_to_string({opaque_type_test, [Fun, Args, Arg, ArgType]}) -> - fmt("~!^The type test ~!!~s~s~!^ breaks the opaqueness of the term " - "~!!~s~s", [Fun, Args, Arg, ArgType]); + fmt("~!^The type test ~!!~ts~ts~!^ breaks the opaqueness of the term " + "~!!~ts~ts", [Fun, Args, Arg, ArgType]); message_to_string({opaque_size, [SizeType, Size]}) -> - fmt("~!^The size ~!!~s~!^ breaks the opaqueness of ~!!~s", + fmt("~!^The size ~!!~ts~!^ breaks the opaqueness of ~!!~ts", [SizeType, Size]); message_to_string({opaque_call, [M, F, Args, Culprit, OpaqueType]}) -> - fmt("~!^The call ~!!~s:~s~s~!^ breaks the opaqueness of the term~!!" - " ~s :: ~s", [M, F, Args, Culprit, OpaqueType]); + fmt("~!^The call ~!!~ts:~ts~ts~!^ breaks the opaqueness of the term~!!" + " ~ts :: ~ts", [M, F, Args, Culprit, OpaqueType]); %%----- Warnings for concurrency errors -------------------- message_to_string({race_condition, [M, F, Args, Reason]}) -> - fmt("~!^The call ~!!~w:~w~s ~s", [M, F, Args, Reason]); + fmt("~!^The call ~!!~w:~w~ts ~ts", [M, F, Args, Reason]); %%----- Warnings for behaviour errors -------------------- message_to_string({callback_type_mismatch, [B, F, A, ST, CT]}) -> - fmt("~!^The inferred return type of~!! ~w/~w (~s) ~!^" - "has nothing in common with~!! ~s, ~!^which is the expected" + fmt("~!^The inferred return type of~!! ~w/~w (~ts) ~!^" + "has nothing in common with~!! ~ts, ~!^which is the expected" " return type for the callback of~!! ~w ~!^behaviour", [F, A, ST, CT, B]); message_to_string({callback_arg_type_mismatch, [B, F, A, N, ST, CT]}) -> - fmt("~!^The inferred type for the~!! ~s ~!^argument of~!!" - " ~w/~w (~s) ~!^is not a supertype of~!! ~s~!^, which is" + fmt("~!^The inferred type for the~!! ~ts ~!^argument of~!!" + " ~w/~w (~ts) ~!^is not a supertype of~!! ~ts~!^, which is" "expected type for this argument in the callback of the~!! ~w " "~!^behaviour", [ordinal(N), F, A, ST, CT, B]); message_to_string({callback_spec_type_mismatch, [B, F, A, ST, CT]}) -> - fmt("~!^The return type ~!!~s~!^ in the specification of ~!!" - "~w/~w~!^ is not a subtype of ~!!~s~!^, which is the expected" + fmt("~!^The return type ~!!~ts~!^ in the specification of ~!!" + "~w/~w~!^ is not a subtype of ~!!~ts~!^, which is the expected" " return type for the callback of ~!!~w~!^ behaviour", [ST, F, A, CT, B]); message_to_string({callback_spec_arg_type_mismatch, [B, F, A, N, ST, CT]}) -> - fmt("~!^The specified type for the ~!!~s~!^ argument of ~!!" - "~w/~w (~s)~!^ is not a supertype of ~!!~s~!^, which is" + fmt("~!^The specified type for the ~!!~ts~!^ argument of ~!!" + "~w/~w (~ts)~!^ is not a supertype of ~!!~ts~!^, which is" " expected type for this argument in the callback of the ~!!~w" "~!^ behaviour", [ordinal(N), F, A, ST, CT, B]); message_to_string({callback_missing, [B, F, A]}) -> @@ -274,26 +274,26 @@ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, true -> %% We do not know which argument(s) caused the failure fmt("~!^will never return since the success typing arguments" - " are ~!!~s", [SigArgs]); + " are ~!!~ts", [SigArgs]); false -> fmt("~!^will never return since it differs in the~!!" - " ~s ~!^argument from the success typing" - " arguments:~!! ~s", + " ~ts ~!^argument from the success typing" + " arguments:~!! ~ts", [PositionString, good_arg(ArgNs, SigArgs)]) end; only_contract -> case (ArgNs =:= []) orelse IsOverloaded of true -> %% We do not know which arguments caused the failure - fmt("~!^breaks the contract~!! ~s", [good_arg(ArgNs, Contract)]); + fmt("~!^breaks the contract~!! ~ts", [good_arg(ArgNs, Contract)]); false -> - fmt("~!^breaks the contract~!! ~s ~!^in the~!!" - " ~s ~!^argument", + fmt("~!^breaks the contract~!! ~ts ~!^in the~!!" + " ~ts ~!^argument", [good_arg(ArgNs, Contract), PositionString]) end; both -> fmt("~!^will never return since the success typing is " - "~!!~s ~!^->~!! ~s ~!^and the contract is ~!!~s", + "~!!~ts ~!^->~!! ~ts ~!^and the contract is ~!!~ts", [good_arg(ArgNs, SigArgs), SigRet, good_arg(ArgNs, Contract)]) end. @@ -301,8 +301,8 @@ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, form_positions(ArgNs) -> ArgS = form_position_string(ArgNs), case ArgNs of - [_] -> fmt("~!^an opaque term as ~!!~s~!^ argument", [ArgS]); - [_,_|_] -> fmt("~!^opaque terms as ~!!~s~!^ arguments", [ArgS]) + [_] -> fmt("~!^an opaque term as ~!!~ts~!^ argument", [ArgS]); + [_,_|_] -> fmt("~!^opaque terms as ~!!~ts~!^ arguments", [ArgS]) end. %% We know which positions N are to blame; @@ -310,9 +310,9 @@ form_positions(ArgNs) -> form_expected_without_opaque([{N, T, TStr}]) -> FStr = case erl_types:t_is_opaque(T) of true -> - "~!^an opaque term of type~!g ~s ~!^as "; + "~!^an opaque term of type~!g ~ts ~!^as "; false -> - "~!^a term of type ~!g~s ~!^(with opaque subterms) as " + "~!^a term of type ~!g~ts ~!^(with opaque subterms) as " end ++ form_position_string([N]) ++ "~!^ argument", fmt(FStr, [TStr]); @@ -325,9 +325,9 @@ form_expected(ExpectedArgs) -> [T] -> TS = erl_types:t_to_string(T), case erl_types:t_is_opaque(T) of - true -> fmt("~!^an opaque term of type ~!!~s~!^ is" + true -> fmt("~!^an opaque term of type ~!!~ts~!^ is" " expected", [TS]); - false -> fmt("~!^a structured term of type ~!!~s~!^ is" + false -> fmt("~!^a structured term of type ~!!~ts~!^ is" " expected", [TS]) end; [_,_|_] -> fmt("~!^terms of different types are expected in these" @@ -340,7 +340,7 @@ form_position_string(ArgNs) -> [N1] -> ordinal(N1); [_,_|_] -> [Last|Prevs] = lists:reverse(ArgNs), - ", " ++ Head = lists:flatten([fmt(", ~s",[ordinal(N)]) || + ", " ++ Head = lists:flatten([fmt(", ~ts",[ordinal(N)]) || N <- lists:reverse(Prevs)]), Head ++ " and " ++ ordinal(Last) end. @@ -352,11 +352,11 @@ ordinal(N) when is_integer(N) -> fmt("~!B~w~!!th", [N]). %% Format a pattern ad highlight errorous part in red. bad_pat("pattern " ++ P) -> - fmt("pattern ~!r~s",[P]); + fmt("pattern ~!r~ts",[P]); bad_pat("variable " ++ P) -> - fmt("variable ~!r~s",[P]); + fmt("variable ~!r~ts",[P]); bad_pat(P) -> - fmt("~!r~s",[P]). + fmt("~!r~ts",[P]). bad_arg(N, Args) -> @@ -370,7 +370,7 @@ good_arg(N, Args) -> colour_arg(N, C, Args) when is_integer(N) -> colour_arg([N], C, Args); colour_arg(Ns, C, Args) -> - {Args1, Rest} =seperate_args(Args), + {Args1, Rest} =separate_args(Args), Args2 = highlight(Ns, 1, C, Args1), join_args(Args2) ++ Rest. @@ -378,53 +378,53 @@ highlight([], _N, _C, Rest) -> Rest; highlight([N | Nr], N, g, [Arg | Rest]) -> - [fmt("~!g~s", [Arg]) | highlight(Nr, N+1, g, Rest)]; + [fmt("~!g~ts", [Arg]) | highlight(Nr, N+1, g, Rest)]; highlight([N | Nr], N, r, [Arg | Rest]) -> - [fmt("~!r~s", [Arg]) | highlight(Nr, N+1, r, Rest)]; + [fmt("~!r~ts", [Arg]) | highlight(Nr, N+1, r, Rest)]; highlight(Ns, N, C, [Arg | Rest]) -> [Arg | highlight(Ns, N + 1, C, Rest)]. %% Arugments to functions and constraints are passed as %% strings not as data, this function pulls them apart -%% to allow interacting with them seperately and not +%% to allow interacting with them separately and not %% as one bug chunk of data. -seperate_args([$( | S]) -> - seperate_args([], S, "", []). +separate_args([$( | S]) -> + separate_args([], S, "", []). %% We strip this space since dialyzer is inconsistant in adding or not adding %% it .... -seperate_args([], [$,, $\s | R], Arg, Args) -> - seperate_args([], R, [], [lists:reverse(Arg) | Args]); +separate_args([], [$,, $\s | R], Arg, Args) -> + separate_args([], R, [], [lists:reverse(Arg) | Args]); -seperate_args([], [$, | R], Arg, Args) -> - seperate_args([], R, [], [lists:reverse(Arg) | Args]); +separate_args([], [$, | R], Arg, Args) -> + separate_args([], R, [], [lists:reverse(Arg) | Args]); -seperate_args([], [$) | Rest], Arg, Args) -> +separate_args([], [$) | Rest], Arg, Args) -> {lists:reverse([lists:reverse(Arg) | Args]), Rest}; -seperate_args([C | D], [C | R], Arg, Args) -> - seperate_args(D, R, [C | Arg], Args); +separate_args([C | D], [C | R], Arg, Args) -> + separate_args(D, R, [C | Arg], Args); %% Brackets -seperate_args(D, [${ | R], Arg, Args) -> - seperate_args([$}|D], R, [${ | Arg], Args); +separate_args(D, [${ | R], Arg, Args) -> + separate_args([$}|D], R, [${ | Arg], Args); -seperate_args(D, [$( | R], Arg, Args) -> - seperate_args([$)|D], R, [$( | Arg], Args); +separate_args(D, [$( | R], Arg, Args) -> + separate_args([$)|D], R, [$( | Arg], Args); -seperate_args(D, [$[ | R], Arg, Args) -> - seperate_args([$]|D], R, [$[ | Arg], Args); +separate_args(D, [$[ | R], Arg, Args) -> + separate_args([$]|D], R, [$[ | Arg], Args); -seperate_args(D, [$< | R], Arg, Args) -> - seperate_args([$>|D], R, [$< | Arg], Args); +separate_args(D, [$< | R], Arg, Args) -> + separate_args([$>|D], R, [$< | Arg], Args); %% 'strings' -seperate_args(D, [$' | R], Arg, Args) -> - seperate_args([$'|D], R, [$' | Arg], Args); -seperate_args(D, [$" | R], Arg, Args) -> - seperate_args([$"|D], R, [$" | Arg], Args); +separate_args(D, [$' | R], Arg, Args) -> + separate_args([$'|D], R, [$' | Arg], Args); +separate_args(D, [$" | R], Arg, Args) -> + separate_args([$"|D], R, [$" | Arg], Args); -seperate_args(D, [C | R], Arg, Args) -> - seperate_args(D, R, [C | Arg], Args). +separate_args(D, [C | R], Arg, Args) -> + separate_args(D, R, [C | Arg], Args). join_args(Args) -> [$(, string:join(Args, ", "), $)]. diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl index a827735..776d7b8 100644 --- a/src/rebar_digraph.erl +++ b/src/rebar_digraph.erl @@ -53,9 +53,9 @@ add(Graph, {PkgName, Deps}) -> lists:foreach(fun(DepName) -> Name1 = case DepName of {Name, _Vsn} -> - ec_cnv:to_binary(Name); + rebar_utils:to_binary(Name); Name -> - ec_cnv:to_binary(Name) + rebar_utils:to_binary(Name) end, V3 = case digraph:vertex(Graph, Name1) of false -> diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index b61bfcc..d0c7805 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -41,7 +41,7 @@ base_dir(State) -> %% of profiles. -spec profile_dir(rebar_dict(), [atom()]) -> file:filename_all(). profile_dir(Opts, Profiles) -> - {BaseDir, ProfilesStrings} = case [ec_cnv:to_list(P) || P <- Profiles] of + {BaseDir, ProfilesStrings} = case [rebar_utils:to_list(P) || P <- Profiles] of ["global" | _] -> {?MODULE:global_cache_dir(Opts), [""]}; ["bootstrap", "default"] -> {rebar_opts:get(Opts, base_dir, ?DEFAULT_BASE_DIR), ["default"]}; ["default"] -> {rebar_opts:get(Opts, base_dir, ?DEFAULT_BASE_DIR), ["default"]}; @@ -363,7 +363,7 @@ warn_source_format_once(Format) -> ok; true -> ?WARN("Invalid argument ~p for compiler_source_format - " - "assuming ~s~n", [Format, ?DEFAULT_COMPILER_SOURCE_FORMAT]) + "assuming ~ts~n", [Format, ?DEFAULT_COMPILER_SOURCE_FORMAT]) end. %% @private takes a filename and canonicalizes its path if it is a link. diff --git a/src/rebar_dist_utils.erl b/src/rebar_dist_utils.erl index 93edf9d..5de858e 100644 --- a/src/rebar_dist_utils.erl +++ b/src/rebar_dist_utils.erl @@ -51,14 +51,27 @@ find_options(State) -> %%% PRIVATE %%% %%%%%%%%%%%%%%% start(Name, Type, Opts) -> - check_epmd(net_kernel:start([Name, Type])), + case dist_up(net_kernel:start([Name, Type])) of + false -> + start_epmd(), + dist_up(net_kernel:start([Name, Type])) orelse warn_dist(); + true -> + ok + end, setup_cookie(Opts). -check_epmd({error,{{shutdown, {_,net_kernel,{'EXIT',nodistribution}}},_}}) -> - ?ERROR("Erlang Distribution failed, falling back to nonode@nohost. " - "Verify that epmd is running and try again.",[]); -check_epmd(_) -> - ok. +dist_up({error,{{shutdown,{_,net_kernel,{'EXIT',nodistribution}}},_}}) -> false; +dist_up(_) -> true. + +start_epmd() -> + %% Indirectly boot EPMD through calling Erlang so that we don't risk + %% attaching it to the current proc + ?CONSOLE("Attempting to start epmd...", []), + os:cmd("erl -sname a -eval 'halt(0).'"). + +warn_dist() -> + ?ERROR("Erlang Distribution failed, falling back to nonode@nohost.", []). + setup_cookie(Opts) -> case {node(), proplists:get_value(setcookie, Opts, nocookie)} of diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index f7244dc..94cbe13 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -92,7 +92,7 @@ compile(AppInfo) when element(1, AppInfo) == app_info_t -> %% @doc compile an individual application. -spec compile(rebar_app_info:t(), compile_opts()) -> ok. compile(AppInfo, CompileOpts) when element(1, AppInfo) == app_info_t -> - Dir = ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)), + Dir = rebar_utils:to_list(rebar_app_info:out_dir(AppInfo)), RebarOpts = rebar_app_info:opts(AppInfo), SrcOpts = [check_last_mod, @@ -237,8 +237,8 @@ clean(AppInfo) -> YrlFiles = rebar_utils:find_files(filename:join([AppDir, "src"]), ?RE_PREFIX".*\\.[x|y]rl\$"), rebar_file_utils:delete_each( - [ binary_to_list(iolist_to_binary(re:replace(F, "\\.[x|y]rl$", ".erl"))) - || F <- YrlFiles ]), + [rebar_utils:to_list(re:replace(F, "\\.[x|y]rl$", ".erl", [unicode])) + || F <- YrlFiles]), BinDirs = ["ebin"|rebar_dir:extra_src_dirs(rebar_app_info:opts(AppInfo))], ok = clean_dirs(AppDir, BinDirs), @@ -279,7 +279,7 @@ gather_src(Opts, BaseDirParts, [Dir|Rest], Srcs, CompileOpts) -> end, DirRecursive = dir_recursive(Opts, RelDir, CompileOpts), gather_src(Opts, BaseDirParts, Rest, Srcs ++ rebar_utils:find_files(Dir, ?RE_PREFIX".*\\.erl\$", DirRecursive), CompileOpts). - + %% Get files which need to be compiled first, i.e. those specified in erl_first_files %% and parse_transform options. Also produce specific erl_opts for these first %% files, so that yet to be compiled parse transformations are excluded from it. @@ -339,7 +339,7 @@ maybe_rm_beam_and_edge(G, OutDir, Source) -> false; false -> Target = target_base(OutDir, Source) ++ ".beam", - ?DEBUG("Source ~s is gone, deleting previous beam file if it exists ~s", [Source, Target]), + ?DEBUG("Source ~ts is gone, deleting previous beam file if it exists ~ts", [Source, Target]), file:delete(Target), digraph:del_vertex(G, Source), true @@ -351,10 +351,25 @@ opts_changed(NewOpts, Target) -> false -> NewOpts end, case compile_info(Target) of - {ok, Opts} -> lists:sort(Opts) =/= lists:sort(TotalOpts); + {ok, Opts} -> lists:any(fun effects_code_generation/1, lists:usort(TotalOpts) -- lists:usort(Opts)); _ -> true end. +effects_code_generation(Option) -> + case Option of + beam -> false; + report_warnings -> false; + report_errors -> false; + return_errors-> false; + return_warnings-> false; + warnings_as_errors -> false; + binary -> false; + verbose -> false; + {cwd,_} -> false; + {outdir, _} -> false; + _ -> true + end. + compile_info(Target) -> case beam_lib:chunks(Target, [compile_info]) of {ok, {_mod, Chunks}} -> @@ -385,7 +400,7 @@ init_erlcinfo(InclDirs, Erls, Dir, OutDir) -> try restore_erlcinfo(G, InclDirs, Dir) catch _:_ -> - ?WARN("Failed to restore ~s file. Discarding it.~n", [erlcinfo_file(Dir)]), + ?WARN("Failed to restore ~ts file. Discarding it.~n", [erlcinfo_file(Dir)]), file:delete(erlcinfo_file(Dir)) end, Dirs = source_and_include_dirs(InclDirs, Erls), diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl index 47bfe1d..f68a54d 100644 --- a/src/rebar_fetch.erl +++ b/src/rebar_fetch.erl @@ -41,12 +41,12 @@ download_source_(AppDir, Source, State) -> Resources = rebar_state:resources(State), Module = get_resource_type(Source, Resources), TmpDir = ec_file:insecure_mkdtemp(), - AppDir1 = ec_cnv:to_list(AppDir), + AppDir1 = rebar_utils:to_list(AppDir), case Module:download(TmpDir, Source, State) of {ok, _} -> ec_file:mkdir_p(AppDir1), code:del_path(filename:absname(filename:join(AppDir1, "ebin"))), - ec_file:remove(filename:absname(AppDir1), [recursive]), + ok = rebar_file_utils:rm_rf(filename:absname(AppDir1)), ?DEBUG("Moving checkout ~p to ~p", [TmpDir, filename:absname(AppDir1)]), ok = rebar_file_utils:mv(TmpDir, filename:absname(AppDir1)), true; @@ -66,25 +66,25 @@ needs_update(AppDir, Source, State) -> end. format_error({bad_download, CachePath}) -> - io_lib:format("Download of package does not match md5sum from server: ~s", [CachePath]); + io_lib:format("Download of package does not match md5sum from server: ~ts", [CachePath]); format_error({unexpected_hash, CachePath, Expected, Found}) -> - io_lib:format("The checksum for package at ~s (~s) does not match the " - "checksum previously locked (~s). Either unlock or " + io_lib:format("The checksum for package at ~ts (~ts) does not match the " + "checksum previously locked (~ts). Either unlock or " "upgrade the package, or make sure you fetched it from " "the same index from which it was initially fetched.", [CachePath, Found, Expected]); format_error({failed_extract, CachePath}) -> - io_lib:format("Failed to extract package: ~s", [CachePath]); + io_lib:format("Failed to extract package: ~ts", [CachePath]); format_error({bad_etag, Source}) -> - io_lib:format("MD5 Checksum comparison failed for: ~s", [Source]); + io_lib:format("MD5 Checksum comparison failed for: ~ts", [Source]); format_error({fetch_fail, Name, Vsn}) -> - io_lib:format("Failed to fetch and copy dep: ~s-~s", [Name, Vsn]); + io_lib:format("Failed to fetch and copy dep: ~ts-~ts", [Name, Vsn]); format_error({fetch_fail, Source}) -> io_lib:format("Failed to fetch and copy dep: ~p", [Source]); format_error({bad_checksum, File}) -> - io_lib:format("Checksum mismatch against tarball in ~s", [File]); + io_lib:format("Checksum mismatch against tarball in ~ts", [File]); format_error({bad_registry_checksum, File}) -> - io_lib:format("Checksum mismatch against registry in ~s", [File]). + io_lib:format("Checksum mismatch against registry in ~ts", [File]). get_resource_type({Type, Location}, Resources) -> find_resource_module(Type, Location, Resources); @@ -100,7 +100,7 @@ find_resource_module(Type, Location, Resources) -> false -> case code:which(Type) of non_existing -> - {error, io_lib:format("Cannot handle dependency ~s.~n" + {error, io_lib:format("Cannot handle dependency ~ts.~n" " No module for resource type ~p", [Location, Type])}; _ -> Type diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 8158312..d7716e5 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -84,7 +84,7 @@ consult_config(State, Filename) -> [JoinedConfig]. format_error({bad_term_file, AppFile, Reason}) -> - io_lib:format("Error reading file ~s: ~s", [AppFile, file:format_error(Reason)]). + io_lib:format("Error reading file ~ts: ~ts", [AppFile, file:format_error(Reason)]). symlink_or_copy(Source, Target) -> Link = case os:type() of @@ -121,7 +121,7 @@ symlink_or_copy(Source, Target) -> win32_symlink(Source, Target) -> Res = rebar_utils:sh( - ?FMT("cmd /c mklink /j \"~s\" \"~s\"", + ?FMT("cmd /c mklink /j \"~ts\" \"~ts\"", [rebar_utils:escape_double_quotes(filename:nativename(Target)), rebar_utils:escape_double_quotes(filename:nativename(Source))]), [{use_stdout, false}, return_on_error]), @@ -129,7 +129,7 @@ win32_symlink(Source, Target) -> true -> ok; false -> {error, lists:flatten( - io_lib:format("Failed to symlink ~s to ~s~n", + io_lib:format("Failed to symlink ~ts to ~ts~n", [Source, Target]))} end. @@ -141,7 +141,7 @@ rm_rf(Target) -> case os:type() of {unix, _} -> EscTarget = rebar_utils:escape_chars(Target), - {ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]), + {ok, []} = rebar_utils:sh(?FMT("rm -rf ~ts", [EscTarget]), [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> @@ -161,7 +161,7 @@ cp_r(Sources, Dest) -> {unix, _} -> EscSources = [rebar_utils:escape_chars(Src) || Src <- Sources], SourceStr = string:join(EscSources, " "), - {ok, []} = rebar_utils:sh(?FMT("cp -Rp ~s \"~s\"", + {ok, []} = rebar_utils:sh(?FMT("cp -Rp ~ts \"~ts\"", [SourceStr, rebar_utils:escape_double_quotes(Dest)]), [{use_stdout, false}, abort_on_error]), ok; @@ -176,7 +176,7 @@ mv(Source, Dest) -> {unix, _} -> EscSource = rebar_utils:escape_chars(Source), EscDest = rebar_utils:escape_chars(Dest), - case rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), + case rebar_utils:sh(?FMT("mv ~ts ~ts", [EscSource, EscDest]), [{use_stdout, false}, abort_on_error]) of {ok, []} -> ok; @@ -234,7 +234,7 @@ robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) -> case ec_file:insecure_mkdtemp() of {error, _Reason} -> {error, lists:flatten( - io_lib:format("Failed to move ~s to ~s (tmpdir failed)~n", + io_lib:format("Failed to move ~ts to ~ts (tmpdir failed)~n", [Source, Dest]))}; TmpPath -> case robocopy_file(SrcDir, TmpPath, SrcName) of @@ -246,7 +246,7 @@ robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) -> case file:rename(TmpSrc, TmpDst) of {error, _} -> {error, lists:flatten( - io_lib:format("Failed to move ~s to ~s (via rename)~n", + io_lib:format("Failed to move ~ts to ~ts (via rename)~n", [Source, Dest]))}; ok -> case robocopy_file(TmpPath, DestDir, DestName) of @@ -258,7 +258,7 @@ robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) -> end. robocopy_file(SrcPath, DestPath, FileName) -> - Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\"", + Cmd = ?FMT("robocopy /move /e \"~ts\" \"~ts\" \"~ts\"", [rebar_utils:escape_double_quotes(SrcPath), rebar_utils:escape_double_quotes(DestPath), rebar_utils:escape_double_quotes(FileName)]), @@ -266,7 +266,7 @@ robocopy_file(SrcPath, DestPath, FileName) -> case win32_ok(Res) of false -> {error, lists:flatten( - io_lib:format("Failed to move ~s to ~s~n", + io_lib:format("Failed to move ~ts to ~ts~n", [filename:join(SrcPath, FileName), filename:join(DestPath, FileName)]))}; true -> @@ -274,7 +274,7 @@ robocopy_file(SrcPath, DestPath, FileName) -> end. robocopy_dir(Source, Dest) -> - Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\"", + Cmd = ?FMT("robocopy /move /e \"~ts\" \"~ts\"", [rebar_utils:escape_double_quotes(Source), rebar_utils:escape_double_quotes(Dest)]), Res = rebar_utils:sh(Cmd, @@ -283,7 +283,7 @@ robocopy_dir(Source, Dest) -> true -> ok; false -> {error, lists:flatten( - io_lib:format("Failed to move ~s to ~s~n", + io_lib:format("Failed to move ~ts to ~ts~n", [Source, Dest]))} end. @@ -301,12 +301,19 @@ delete_each([File | Rest]) -> {error, enoent} -> delete_each(Rest); {error, Reason} -> - ?ERROR("Failed to delete file ~s: ~p\n", [File, Reason]), + ?ERROR("Failed to delete file ~ts: ~p\n", [File, Reason]), ?FAIL end. write_file_if_contents_differ(Filename, Bytes) -> - ToWrite = iolist_to_binary(Bytes), + %% first try to convert directly to binaries, + %% but if it fails, we likely contain unicode and + %% need special treatment + ToWrite = try + iolist_to_binary(Bytes) + catch + error:badarg -> unicode:characters_to_binary(Bytes) + end, case file:read_file(Filename) of {ok, ToWrite} -> ok; @@ -401,13 +408,13 @@ split_dirname(Path) -> delete_each_dir_win32([]) -> ok; delete_each_dir_win32([Dir | Rest]) -> - {ok, []} = rebar_utils:sh(?FMT("rd /q /s \"~s\"", + {ok, []} = rebar_utils:sh(?FMT("rd /q /s \"~ts\"", [rebar_utils:escape_double_quotes(filename:nativename(Dir))]), [{use_stdout, false}, return_on_error]), delete_each_dir_win32(Rest). xcopy_win32(Source,Dest)-> - %% "xcopy \"~s\" \"~s\" /q /y /e 2> nul", Changed to robocopy to + %% "xcopy \"~ts\" \"~ts\" /q /y /e 2> nul", Changed to robocopy to %% handle long names. May have issues with older windows. Cmd = case filelib:is_dir(Source) of true -> @@ -417,11 +424,11 @@ xcopy_win32(Source,Dest)-> %% must manually add the last fragment of a directory to the `Dest` %% in order to properly replicate POSIX platforms NewDest = filename:join([Dest, filename:basename(Source)]), - ?FMT("robocopy \"~s\" \"~s\" /e /is 1> nul", + ?FMT("robocopy \"~ts\" \"~ts\" /e /is 1> nul", [rebar_utils:escape_double_quotes(filename:nativename(Source)), rebar_utils:escape_double_quotes(filename:nativename(NewDest))]); false -> - ?FMT("robocopy \"~s\" \"~s\" \"~s\" /e /is 1> nul", + ?FMT("robocopy \"~ts\" \"~ts\" \"~ts\" /e /is 1> nul", [rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))), rebar_utils:escape_double_quotes(filename:nativename(Dest)), rebar_utils:escape_double_quotes(filename:basename(Source))]) @@ -432,7 +439,7 @@ xcopy_win32(Source,Dest)-> true -> ok; false -> {error, lists:flatten( - io_lib:format("Failed to copy ~s to ~s~n", + io_lib:format("Failed to copy ~ts to ~ts~n", [Source, Dest]))} end. diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index 201b8b6..a6b4d00 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -17,7 +17,7 @@ lock(AppDir, {git, Url, _}) -> lock(AppDir, {git, Url}); lock(AppDir, {git, Url}) -> - AbortMsg = lists:flatten(io_lib:format("Locking of git dependency failed in ~s", [AppDir])), + AbortMsg = lists:flatten(io_lib:format("Locking of git dependency failed in ~ts", [AppDir])), Dir = rebar_utils:escape_double_quotes(AppDir), {ok, VsnString} = case os:type() of @@ -38,39 +38,37 @@ needs_update(Dir, {git, Url, {tag, Tag}}) -> [{cd, Dir}]), Current1 = string:strip(string:strip(Current, both, $\n), both, $\r), - ?DEBUG("Comparing git tag ~s with ~s", [Tag, Current1]), + ?DEBUG("Comparing git tag ~ts with ~ts", [Tag, Current1]), not ((Current1 =:= Tag) andalso compare_url(Dir, Url)); needs_update(Dir, {git, Url, {branch, Branch}}) -> %% Fetch remote so we can check if the branch has changed SafeBranch = rebar_utils:escape_chars(Branch), - {ok, _} = rebar_utils:sh(?FMT("git fetch origin ~s", [SafeBranch]), + {ok, _} = rebar_utils:sh(?FMT("git fetch origin ~ts", [SafeBranch]), [{cd, Dir}]), %% Check for new commits to origin/Branch - {ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~s --oneline", [SafeBranch]), + {ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~ts --oneline", [SafeBranch]), [{cd, Dir}]), - ?DEBUG("Checking git branch ~s for updates", [Branch]), + ?DEBUG("Checking git branch ~ts for updates", [Branch]), not ((Current =:= []) andalso compare_url(Dir, Url)); needs_update(Dir, {git, Url, "master"}) -> needs_update(Dir, {git, Url, {branch, "master"}}); needs_update(Dir, {git, _, Ref}) -> - {ok, Current} = rebar_utils:sh(?FMT("git rev-parse -q HEAD", []), + {ok, Current} = rebar_utils:sh(?FMT("git rev-parse --short=7 -q HEAD", []), [{cd, Dir}]), Current1 = string:strip(string:strip(Current, both, $\n), both, $\r), Ref2 = case Ref of {ref, Ref1} -> Length = length(Current1), - if - Length >= 7 -> - lists:sublist(Ref1, Length); - true -> - Ref1 + case Length >= 7 of + true -> lists:sublist(Ref1, Length); + false -> Ref1 end; - Ref1 -> - Ref1 + _ -> + Ref end, - ?DEBUG("Comparing git ref ~s with ~s", [Ref1, Current1]), + ?DEBUG("Comparing git ref ~ts with ~ts", [Ref2, Current1]), (Current1 =/= Ref2). compare_url(Dir, Url) -> @@ -84,7 +82,7 @@ compare_url(Dir, Url) -> parse_git_url(Url) -> %% Checks for standard scp style git remote - case re:run(Url, ?SCP_PATTERN, [{capture, [host, path], list}]) of + case re:run(Url, ?SCP_PATTERN, [{capture, [host, path], list}, unicode]) of {match, [Host, Path]} -> {ok, {Host, filename:rootname(Path, ".git")}}; nomatch -> @@ -121,41 +119,41 @@ download(Dir, {git, Url, Rev}, _State) -> %% Use different git clone commands depending on git --version git_clone(branch,Vsn,Url,Dir,Branch) when Vsn >= {1,7,10}; Vsn =:= undefined -> - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", + rebar_utils:sh(?FMT("git clone ~ts ~ts -b ~ts --single-branch", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir)), rebar_utils:escape_chars(Branch)]), [{cd, filename:dirname(Dir)}]); git_clone(branch,_Vsn,Url,Dir,Branch) -> - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s", + rebar_utils:sh(?FMT("git clone ~ts ~ts -b ~ts", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir)), rebar_utils:escape_chars(Branch)]), [{cd, filename:dirname(Dir)}]); git_clone(tag,Vsn,Url,Dir,Tag) when Vsn >= {1,7,10}; Vsn =:= undefined -> - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", + rebar_utils:sh(?FMT("git clone ~ts ~ts -b ~ts --single-branch", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir)), rebar_utils:escape_chars(Tag)]), [{cd, filename:dirname(Dir)}]); git_clone(tag,_Vsn,Url,Dir,Tag) -> - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s", + rebar_utils:sh(?FMT("git clone ~ts ~ts -b ~ts", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir)), rebar_utils:escape_chars(Tag)]), [{cd, filename:dirname(Dir)}]); git_clone(ref,_Vsn,Url,Dir,Ref) -> - rebar_utils:sh(?FMT("git clone -n ~s ~s", + rebar_utils:sh(?FMT("git clone -n ~ts ~ts", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]), - rebar_utils:sh(?FMT("git checkout -q ~s", [Ref]), [{cd, Dir}]); + rebar_utils:sh(?FMT("git checkout -q ~ts", [Ref]), [{cd, Dir}]); git_clone(rev,_Vsn,Url,Dir,Rev) -> - rebar_utils:sh(?FMT("git clone -n ~s ~s", + rebar_utils:sh(?FMT("git clone -n ~ts ~ts", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]), - rebar_utils:sh(?FMT("git checkout -q ~s", [rebar_utils:escape_chars(Rev)]), + rebar_utils:sh(?FMT("git checkout -q ~ts", [rebar_utils:escape_chars(Rev)]), [{cd, Dir}]). git_vsn() -> @@ -170,7 +168,7 @@ git_vsn() -> git_vsn_fetch() -> case rebar_utils:sh("git --version",[]) of {ok, VsnStr} -> - case re:run(VsnStr, "git version\\h+(\\d)\\.(\\d)\\.(\\d).*",[{capture,[1,2,3],list}]) of + case re:run(VsnStr, "git version\\h+(\\d)\\.(\\d)\\.(\\d).*", [{capture,[1,2,3],list}, unicode]) of {match,[Maj,Min,Patch]} -> {list_to_integer(Maj), list_to_integer(Min), @@ -200,7 +198,7 @@ collect_default_refcount(Dir) -> return_on_error, {cd, Dir}]) of {error, _} -> - ?WARN("Getting log of git dependency failed in ~s. Falling back to version 0.0.0", [rebar_dir:get_cwd()]), + ?WARN("Getting log of git dependency failed in ~ts. Falling back to version 0.0.0", [rebar_dir:get_cwd()]), {plain, "0.0.0"}; {ok, String} -> RawRef = string:strip(String, both, $\n), @@ -224,21 +222,20 @@ collect_default_refcount(Dir) -> build_vsn_string(Vsn, RawRef, Count) -> %% Cleanup the tag and the Ref information. Basically leading 'v's and %% whitespace needs to go away. - RefTag = [".ref", re:replace(RawRef, "\\s", "", [global])], + RefTag = [".ref", re:replace(RawRef, "\\s", "", [global, unicode])], %% Create the valid [semver](http://semver.org) version from the tag case Count of 0 -> - erlang:binary_to_list(erlang:iolist_to_binary(Vsn)); + rebar_utils:to_list(Vsn); _ -> - erlang:binary_to_list(erlang:iolist_to_binary([Vsn, "+build.", - integer_to_list(Count), RefTag])) + rebar_utils:to_list([Vsn, "+build.", integer_to_list(Count), RefTag]) end. 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 = re:replace(RawRef, "\\s", "", [global, unicode]), + Cmd = io_lib:format("git rev-list ~ts..HEAD", [rebar_utils:escape_chars(Ref)]), {ok, PatchLines} = rebar_utils:sh(Cmd, [{use_stdout, false}, @@ -254,7 +251,7 @@ parse_tags(Dir) -> {error, _} -> {undefined, "0.0.0"}; {ok, Line} -> - case re:run(Line, "(\\(|\\s)(HEAD[^,]*,\\s)tag:\\s(v?([^,\\)]+))", [{capture, [3, 4], list}]) of + case re:run(Line, "(\\(|\\s)(HEAD[^,]*,\\s)tag:\\s(v?([^,\\)]+))", [{capture, [3, 4], list}, unicode]) of {match,[Tag, Vsn]} -> {Tag, Vsn}; nomatch -> diff --git a/src/rebar_hg_resource.erl b/src/rebar_hg_resource.erl index 7d03eda..42e634c 100644 --- a/src/rebar_hg_resource.erl +++ b/src/rebar_hg_resource.erl @@ -22,7 +22,7 @@ lock(AppDir, {hg, Url}) -> needs_update(Dir, {hg, Url, {tag, Tag}}) -> Ref = get_ref(Dir), {ClosestTag, Distance} = get_tag_distance(Dir, Ref), - ?DEBUG("Comparing hg tag ~s with ref ~s (closest tag is ~s at distance ~s)", + ?DEBUG("Comparing hg tag ~ts with ref ~ts (closest tag is ~ts at distance ~ts)", [Tag, Ref, ClosestTag, Distance]), not ((Distance =:= "0") andalso (Tag =:= ClosestTag) andalso compare_url(Dir, Url)); @@ -45,7 +45,7 @@ needs_update(Dir, {hg, Url, Ref}) -> Ref1 -> Ref1 end, - ?DEBUG("Comparing hg ref ~s with ~s", [Ref1, LocalRef]), + ?DEBUG("Comparing hg ref ~ts with ~ts", [Ref1, LocalRef]), not ((LocalRef =:= TargetRef) andalso compare_url(Dir, Url)). download(Dir, {hg, Url}, State) -> @@ -56,28 +56,28 @@ download(Dir, {hg, Url, ""}, State) -> download(Dir, {hg, Url, {branch, "default"}}, State); download(Dir, {hg, Url, {branch, Branch}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("hg clone -q -b ~s ~s ~s", + rebar_utils:sh(?FMT("hg clone -q -b ~ts ~ts ~ts", [rebar_utils:escape_chars(Branch), rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]); download(Dir, {hg, Url, {tag, Tag}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("hg clone -q -u ~s ~s ~s", + rebar_utils:sh(?FMT("hg clone -q -u ~ts ~ts ~ts", [rebar_utils:escape_chars(Tag), rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]); download(Dir, {hg, Url, {ref, Ref}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("hg clone -q -r ~s ~s ~s", + rebar_utils:sh(?FMT("hg clone -q -r ~ts ~ts ~ts", [rebar_utils:escape_chars(Ref), rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]); download(Dir, {hg, Url, Rev}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("hg clone -q -r ~s ~s ~s", + rebar_utils:sh(?FMT("hg clone -q -r ~ts ~ts ~ts", [rebar_utils:escape_chars(Rev), rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), @@ -88,7 +88,7 @@ make_vsn(Dir) -> Ref = get_ref(Dir), Cmd = BaseHg ++ "log --template \"{latesttag}+build.{latesttagdistance}.rev.{node|short}\"" " --rev " ++ Ref, - AbortMsg = io_lib:format("Version resolution of hg dependency failed in ~s", [Dir]), + AbortMsg = io_lib:format("Version resolution of hg dependency failed in ~ts", [Dir]), {ok, VsnString} = rebar_utils:sh(Cmd, [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), @@ -108,14 +108,14 @@ compare_url(Dir, Url) -> parse_hg_url(CurrentUrl1) =:= parse_hg_url(Url). get_ref(Dir) -> - AbortMsg = io_lib:format("Get ref of hg dependency failed in ~s", [Dir]), + AbortMsg = io_lib:format("Get ref of hg dependency failed in ~ts", [Dir]), {ok, RefString} = rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" --debug id -i", [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), string:strip(RefString, both, $\n). get_tag_distance(Dir, Ref) -> - AbortMsg = io_lib:format("Get tag distance of hg dependency failed in ~s", [Dir]), + AbortMsg = io_lib:format("Get tag distance of hg dependency failed in ~ts", [Dir]), {ok, LogString} = rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" " "log --template \"{latesttag}-{latesttagdistance}\n\" " @@ -123,11 +123,12 @@ get_tag_distance(Dir, Ref) -> [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), Log = string:strip(LogString, both, $\n), - [Tag, Distance] = re:split(Log, "-([0-9]+)$", [{parts,0}, {return, list}]), + [Tag, Distance] = re:split(Log, "-([0-9]+)$", + [{parts,0}, {return,list}, unicode]), {Tag, Distance}. get_branch_ref(Dir, Branch) -> - AbortMsg = io_lib:format("Get branch ref of hg dependency failed in ~s", [Dir]), + AbortMsg = io_lib:format("Get branch ref of hg dependency failed in ~ts", [Dir]), {ok, BranchRefString} = rebar_utils:sh("hg -R \"" ++ rebar_utils:escape_double_quotes(Dir) ++ "\" log --template \"{node}\n\" --rev " ++ rebar_utils:escape_chars(Branch), diff --git a/src/rebar_hooks.erl b/src/rebar_hooks.erl index d6a0e2b..48aa928 100644 --- a/src/rebar_hooks.erl +++ b/src/rebar_hooks.erl @@ -57,9 +57,9 @@ run_provider_hooks_(Dir, Type, Command, Providers, TypeHooks, State) -> end. format_error({bad_provider, Type, Command, {Name, Namespace}}) -> - io_lib:format("Unable to run ~s hooks for '~p', command '~p' in namespace '~p' not found.", [Type, Command, Namespace, Name]); + io_lib:format("Unable to run ~ts hooks for '~p', command '~p' in namespace '~p' not found.", [Type, Command, Namespace, Name]); format_error({bad_provider, Type, Command, Name}) -> - io_lib:format("Unable to run ~s hooks for '~p', command '~p' not found.", [Type, Command, Name]). + io_lib:format("Unable to run ~ts hooks for '~p', command '~p' not found.", [Type, Command, Name]). %% @doc The following environment variables are exported when running %% a hook (absolute paths): @@ -143,7 +143,7 @@ join_dirs(BaseDir, Dirs) -> string:join([ filename:join(BaseDir, Dir) || Dir <- Dirs ], ":"). re_version(Path) -> - case re:run(Path, "^.*-(?<VER>[^/-]*)$", [{capture, [1], list}]) of + case re:run(Path, "^.*-(?<VER>[^/-]*)$", [{capture,[1],list}, unicode]) of nomatch -> ""; {match, [Ver]} -> Ver end. diff --git a/src/rebar_log.erl b/src/rebar_log.erl index b1a70c2..9150346 100644 --- a/src/rebar_log.erl +++ b/src/rebar_log.erl @@ -57,6 +57,8 @@ intensity() -> high; "low" -> low; + "none" -> + none; _ -> ?DFLT_INTENSITY end, @@ -92,7 +94,7 @@ get_level() -> log(Level = error, Str, Args) -> {ok, LogState} = application:get_env(rebar, log), - ec_cmd_log:Level(LogState, lists:flatten(cf:format("~!^~s~n", [Str])), Args); + ec_cmd_log:Level(LogState, lists:flatten(cf:format("~!^~ts~n", [Str])), Args); log(Level, Str, Args) -> {ok, LogState} = application:get_env(rebar, log), ec_cmd_log:Level(LogState, Str++"~n", Args). diff --git a/src/rebar_opts.erl b/src/rebar_opts.erl index 444b760..589dbb8 100644 --- a/src/rebar_opts.erl +++ b/src/rebar_opts.erl @@ -118,6 +118,10 @@ merge_opt({plugins, _}, NewValue, _OldValue) -> NewValue; merge_opt(profiles, NewValue, OldValue) -> dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); +merge_opt(erl_first_files, Value, Value) -> + Value; +merge_opt(erl_first_files, NewValue, OldValue) -> + OldValue ++ NewValue; merge_opt(mib_first_files, Value, Value) -> Value; merge_opt(mib_first_files, NewValue, OldValue) -> diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl index ddaa44b..ed573f2 100644 --- a/src/rebar_otp_app.erl +++ b/src/rebar_otp_app.erl @@ -58,11 +58,11 @@ compile(State, App) -> validate_app(State, App1). format_error({missing_app_file, Filename}) -> - io_lib:format("App file is missing: ~s", [Filename]); + io_lib:format("App file is missing: ~ts", [Filename]); format_error({file_read, File, Reason}) -> - io_lib:format("Failed to read required file ~s for processing: ~s", [File, file:format_error(Reason)]); + io_lib:format("Failed to read required file ~ts for processing: ~ts", [File, file:format_error(Reason)]); format_error({invalid_name, File, AppName}) -> - io_lib:format("Invalid ~s: name of application (~p) must match filename.", [File, AppName]). + io_lib:format("Invalid ~ts: name of application (~p) must match filename.", [File, AppName]). %% =================================================================== %% Internal functions @@ -222,7 +222,7 @@ app_vsn(AppData, AppFile, State) -> get_value(Key, AppInfo, AppFile) -> case proplists:get_value(Key, AppInfo) of undefined -> - ?ABORT("Failed to get app value '~p' from '~s'~n", [Key, AppFile]); + ?ABORT("Failed to get app value '~p' from '~ts'~n", [Key, AppFile]); Value -> Value end. diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl index 4cce5a8..cba1d16 100644 --- a/src/rebar_packages.erl +++ b/src/rebar_packages.erl @@ -72,12 +72,12 @@ deps(Name, Vsn, State) -> deps_(Name, Vsn, State) -> ?MODULE:verify_table(State), - ets:lookup_element(?PACKAGE_TABLE, {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}, 2). + ets:lookup_element(?PACKAGE_TABLE, {rebar_utils:to_binary(Name), rebar_utils:to_binary(Vsn)}, 2). handle_missing_package(Dep, State, Fun) -> case Dep of {Name, Vsn} -> - ?INFO("Package ~s-~s not found. Fetching registry updates and trying again...", [Name, Vsn]); + ?INFO("Package ~ts-~ts not found. Fetching registry updates and trying again...", [Name, Vsn]); _ -> ?INFO("Package ~p not found. Fetching registry updates and trying again...", [Dep]) end, @@ -128,7 +128,7 @@ registry_checksum({pkg, Name, Vsn, _Hash}, State) -> ets:lookup_element(?PACKAGE_TABLE, {Name, Vsn}, 3) catch _:_ -> - throw(?PRV_ERROR({missing_package, ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)})) + throw(?PRV_ERROR({missing_package, rebar_utils:to_binary(Name), rebar_utils:to_binary(Vsn)})) end. %% Hex supports use of ~> to specify the version required for a dependency. @@ -207,17 +207,17 @@ handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint) -> false -> case {Pkg, PkgVsn} of {undefined, undefined} -> - ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. " + ?DEBUG("Only existing version of ~ts is ~ts which does not match constraint ~~> ~ts. " "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]); _ -> - ?WARN("[~s:~s] Only existing version of ~s is ~s which does not match constraint ~~> ~s. " + ?DEBUG("[~ts:~ts] Only existing version of ~ts is ~ts which does not match constraint ~~> ~ts. " "Using anyway, but it is not guaranteed to work.", [Pkg, PkgVsn, Dep, Vsn, Constraint]) end, {ok, Vsn} end. format_error({missing_package, Name, Vsn}) -> - io_lib:format("Package not found in registry: ~s-~s.", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]); + io_lib:format("Package not found in registry: ~ts-~ts.", [rebar_utils:to_binary(Name), rebar_utils:to_binary(Vsn)]); format_error({missing_package, Dep}) -> io_lib:format("Package not found in registry: ~p.", [Dep]). diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 88419bd..d588f24 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -21,7 +21,7 @@ lock(_AppDir, Source) -> needs_update(Dir, {pkg, _Name, Vsn, _Hash}) -> [AppInfo] = rebar_app_discover:find_apps([Dir], all), - case rebar_app_info:original_vsn(AppInfo) =:= ec_cnv:to_list(Vsn) of + case rebar_app_info:original_vsn(AppInfo) =:= rebar_utils:to_list(Vsn) of true -> false; false -> @@ -43,13 +43,13 @@ download(TmpDir, Pkg={pkg, Name, Vsn, _Hash}, State) -> cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _Hash}, Url, ETag, State) -> case request(Url, ETag) of {ok, cached} -> - ?INFO("Version cached at ~s is up to date, reusing it", [CachePath]), + ?INFO("Version cached at ~ts is up to date, reusing it", [CachePath]), serve_from_cache(TmpDir, CachePath, Pkg, State); {ok, Body, NewETag} -> - ?INFO("Downloaded package, caching at ~s", [CachePath]), + ?INFO("Downloaded package, caching at ~ts", [CachePath]), serve_from_download(TmpDir, CachePath, Pkg, NewETag, Body, State); error when ETag =/= false -> - ?INFO("Download error, using cached file at ~s", [CachePath]), + ?INFO("Download error, using cached file at ~ts", [CachePath]), serve_from_cache(TmpDir, CachePath, Pkg, State); error -> {fetch_fail, Name, Vsn} @@ -76,13 +76,13 @@ serve_from_cache(TmpDir, CachePath, Pkg, State) -> end. serve_from_download(TmpDir, CachePath, Package, ETag, Binary, State) -> - ?DEBUG("Writing ~p to cache at ~s", [Package, CachePath]), + ?DEBUG("Writing ~p to cache at ~ts", [Package, CachePath]), file:write_file(CachePath, Binary), case etag(CachePath) of ETag -> serve_from_cache(TmpDir, CachePath, Package, State); FileETag -> - ?DEBUG("Downloaded file ~s ETag ~s doesn't match returned ETag ~s", [CachePath, ETag, FileETag]), + ?DEBUG("Downloaded file ~ts ETag ~ts doesn't match returned ETag ~ts", [CachePath, ETag, FileETag]), {bad_download, CachePath} end. @@ -114,11 +114,11 @@ request(Url, ETag) -> [{body_format, binary}], rebar) of {ok, {{_Version, 200, _Reason}, Headers, Body}} -> - ?DEBUG("Successfully downloaded ~s", [Url]), + ?DEBUG("Successfully downloaded ~ts", [Url]), {"etag", ETag1} = lists:keyfind("etag", 1, Headers), {ok, Body, string:strip(ETag1, both, $")}; {ok, {{_Version, 304, _Reason}, _Headers, _Body}} -> - ?DEBUG("Cached copy of ~s still valid", [Url]), + ?DEBUG("Cached copy of ~ts still valid", [Url]), {ok, cached}; {ok, {{_Version, Code, _Reason}, _Headers, _Body}} -> ?DEBUG("Request to ~p failed: status code ~p", [Url, Code]), @@ -154,7 +154,7 @@ ssl_opts(Url) -> ssl_opts(ssl_verify_enabled, Url) -> case check_ssl_version() of true -> - {ok, {_, _, Hostname, _, _, _}} = http_uri:parse(ec_cnv:to_list(Url)), + {ok, {_, _, Hostname, _, _, _}} = http_uri:parse(rebar_utils:to_list(Url)), VerifyFun = {fun ssl_verify_hostname:verify_fun/3, [{check_hostname, Hostname}]}, CACerts = certifi:cacerts(), [{verify, verify_peer}, {depth, 2}, {cacerts, CACerts} diff --git a/src/rebar_prv_app_discovery.erl b/src/rebar_prv_app_discovery.erl index 1954214..3f10a3f 100644 --- a/src/rebar_prv_app_discovery.erl +++ b/src/rebar_prv_app_discovery.erl @@ -49,19 +49,19 @@ do(State) -> -spec format_error(any()) -> iolist(). format_error({multiple_app_files, Files}) -> - io_lib:format("Multiple app files found in one app dir: ~s", [string:join(Files, " and ")]); + io_lib:format("Multiple app files found in one app dir: ~ts", [string:join(Files, " and ")]); format_error({invalid_app_file, File, Reason}) -> case Reason of {Line, erl_parse, Description} -> - io_lib:format("Invalid app file ~s at line ~b: ~p", + io_lib:format("Invalid app file ~ts at line ~b: ~p", [File, Line, lists:flatten(Description)]); _ -> - io_lib:format("Invalid app file ~s: ~p", [File, Reason]) + io_lib:format("Invalid app file ~ts: ~p", [File, Reason]) end; %% Provide a slightly more informative error message for consult of app file failure format_error({rebar_file_utils, {bad_term_file, AppFile, Reason}}) -> - io_lib:format("Error in app file ~s: ~s", [rebar_dir:make_relative_path(AppFile, - rebar_dir:get_cwd()), - file:format_error(Reason)]); + io_lib:format("Error in app file ~ts: ~ts", [rebar_dir:make_relative_path(AppFile, + rebar_dir:get_cwd()), + file:format_error(Reason)]); format_error(Reason) -> io_lib:format("~p", [Reason]). diff --git a/src/rebar_prv_as.erl b/src/rebar_prv_as.erl index e7c6d68..562ce99 100644 --- a/src/rebar_prv_as.erl +++ b/src/rebar_prv_as.erl @@ -64,7 +64,7 @@ args_to_profiles_and_tasks(Args) -> first_profile([]) -> {[], []}; first_profile([ProfileList|Rest]) -> - case re:split(ProfileList, ",", [{return, list}, {parts, 2}]) of + case re:split(ProfileList, ",", [{return, list}, {parts, 2}, unicode]) of %% `foo, bar` [P, ""] -> profiles(Rest, [P]); %% `foo,bar` @@ -75,7 +75,7 @@ first_profile([ProfileList|Rest]) -> profiles([], Acc) -> {lists:reverse(Acc), rebar_utils:args_to_tasks([])}; profiles([ProfileList|Rest], Acc) -> - case re:split(ProfileList, ",", [{return, list}, {parts, 2}]) of + case re:split(ProfileList, ",", [{return, list}, {parts, 2}, unicode]) of %% `foo, bar` [P, ""] -> profiles(Rest, [P|Acc]); %% `foo,bar` @@ -101,5 +101,5 @@ warn_on_empty_profile(Profiles, State) -> ProjectApps = rebar_state:project_apps(State), DefinedProfiles = rebar_state:get(State, profiles, []) ++ lists:flatten([rebar_app_info:get(AppInfo, profiles, []) || AppInfo <- ProjectApps]), - [?WARN("No entry for profile ~s in config.", [Profile]) || + [?WARN("No entry for profile ~ts in config.", [Profile]) || Profile <- Profiles, not(lists:keymember(list_to_atom(Profile), 1, DefinedProfiles))]. diff --git a/src/rebar_prv_bare_compile.erl b/src/rebar_prv_bare_compile.erl index 201620a..6f1ac16 100644 --- a/src/rebar_prv_bare_compile.erl +++ b/src/rebar_prv_bare_compile.erl @@ -29,7 +29,8 @@ init(State) -> {example, ""}, {short_desc, ""}, {desc, ""}, - {opts, [{paths, $p, "paths", string, "Wildcard path of ebin directories to add to code path"}]}])), + {opts, [{paths, $p, "paths", string, "Wildcard paths of ebin directories to add to code path, separated by a colon"}, + {separator, $s, "separator", string, "In case of multiple return paths, the separator character to use to join them."}]}])), {ok, State1}. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. @@ -39,8 +40,9 @@ do(State) -> %% Add code paths from --paths to the beginning of the code path {RawOpts, _} = rebar_state:command_parsed_args(State), Paths = proplists:get_value(paths, RawOpts), - CodePaths = filelib:wildcard(Paths), - code:add_pathsa(CodePaths), + Sep = proplists:get_value(separator, RawOpts, " "), + [ code:add_pathsa(filelib:wildcard(PathWildcard)) + || PathWildcard <- string:tokens(Paths, Sep) ], [AppInfo] = rebar_state:project_apps(State), AppInfo1 = rebar_app_info:out_dir(AppInfo, rebar_dir:get_cwd()), diff --git a/src/rebar_prv_clean.erl b/src/rebar_prv_clean.erl index 8f31fdd..aa0b5af 100644 --- a/src/rebar_prv_clean.erl +++ b/src/rebar_prv_clean.erl @@ -44,7 +44,8 @@ do(State) -> case All of true -> DepsDir = rebar_dir:deps_dir(State1), - AllApps = rebar_app_discover:find_apps([filename:join(DepsDir, "*")], all), + DepsDirs = filelib:wildcard(filename:join(DepsDir, "*")), + AllApps = rebar_app_discover:find_apps(DepsDirs, all), clean_apps(State1, Providers, AllApps); false -> ProjectApps = rebar_state:project_apps(State1), @@ -67,7 +68,7 @@ format_error(Reason) -> clean_apps(State, Providers, Apps) -> [begin - ?INFO("Cleaning out ~s...", [rebar_app_info:name(AppInfo)]), + ?INFO("Cleaning out ~ts...", [rebar_app_info:name(AppInfo)]), AppDir = rebar_app_info:dir(AppInfo), AppInfo1 = rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, AppInfo, State), rebar_erlc_compiler:clean(AppInfo1), diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 2ac8fc7..bf788d2 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -41,7 +41,14 @@ do(State) -> Tests = prepare_tests(State), case compile(State, Tests) of %% successfully compiled apps - {ok, S} -> do(S, Tests); + {ok, S} -> + {RawOpts, _} = rebar_state:command_parsed_args(S), + case proplists:get_value(compile_only, RawOpts, false) of + true -> + {ok, S}; + false -> + do(S, Tests) + end; %% this should look like a compiler error, not a ct error Error -> Error end. @@ -93,7 +100,7 @@ format_error({error, Reason}) -> format_error({error_running_tests, Reason}) -> format_error({error, Reason}); format_error({failures_running_tests, {Failed, AutoSkipped}}) -> - io_lib:format("Failures occured running tests: ~b", [Failed+AutoSkipped]); + io_lib:format("Failures occurred running tests: ~b", [Failed+AutoSkipped]); format_error({badconfig, {Msg, {Value, Key}}}) -> io_lib:format(Msg, [Value, Key]); format_error({badconfig, Msg}) -> @@ -431,18 +438,21 @@ test_dirs(State, Apps, Opts) -> set_compile_dirs(State, Apps, join(Suites, Dir)); {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} end; - Specs0 -> - case get_dirs_from_specs(Specs0) of - {ok,{Specs,SuiteDirs}} -> - {State1,Apps1} = set_compile_dirs1(State, Apps, - {dir, SuiteDirs}), - {State2,Apps2} = set_compile_dirs1(State1, Apps1, - {spec, Specs}), - [maybe_copy_spec(State2,Apps2,S) || S <- Specs], - {ok, rebar_state:project_apps(State2, Apps2)}; - Error -> - Error - end + Spec when is_integer(hd(Spec)) -> + spec_test_dirs(State, Apps, [Spec]); + Specs -> + spec_test_dirs(State, Apps, Specs) + end. + +spec_test_dirs(State, Apps, Specs0) -> + case get_dirs_from_specs(Specs0) of + {ok,{Specs,SuiteDirs}} -> + {State1,Apps1} = set_compile_dirs1(State, Apps, {dir, SuiteDirs}), + {State2,Apps2} = set_compile_dirs1(State1, Apps1, {spec, Specs}), + [maybe_copy_spec(State2,Apps2,S) || S <- Specs], + {ok, rebar_state:project_apps(State2, Apps2)}; + Error -> + Error end. join(Suite, Dir) when is_integer(hd(Suite)) -> @@ -564,9 +574,6 @@ get_tests_from_specs(Specs) -> case ct_testspec:collect_tests_from_file(Specs,true) of Tests when is_list(Tests) -> {ok,[{S,ct_testspec:prepare_tests(R)} || {S,R} <- Tests]}; - R when is_tuple(R), element(1,R)==testspec -> - %% R15 - {ok,[{Specs,ct_testspec:prepare_tests(R)}]}; Error -> Error end @@ -650,7 +657,11 @@ handle_results(_) -> sum_results({Passed, Failed, {UserSkipped, AutoSkipped}}, {Passed2, Failed2, {UserSkipped2, AutoSkipped2}}) -> {Passed+Passed2, Failed+Failed2, - {UserSkipped+UserSkipped2, AutoSkipped+AutoSkipped2}}. + {UserSkipped+UserSkipped2, AutoSkipped+AutoSkipped2}}; +sum_results(_, {error, Reason}) -> + {error, Reason}; +sum_results(Unknown, _) -> + {error, Unknown}. handle_quiet_results(_, {error, _} = Result) -> handle_results(Result); @@ -673,7 +684,10 @@ format_result({Passed, 0, {0, 0}}) -> format_result({Passed, Failed, Skipped}) -> Format = [format_failed(Failed), format_skipped(Skipped), format_passed(Passed)], - ?CONSOLE("~s", [Format]). + ?CONSOLE("~ts", [Format]); +format_result(_Unknown) -> + %% Happens when CT itself encounters a bug + ok. format_failed(0) -> []; @@ -705,17 +719,17 @@ maybe_write_coverdata(State) -> rebar_prv_cover:maybe_write_coverdata(State1, ?PROVIDER). ct_opts(_State) -> - [{dir, undefined, "dir", string, help(dir)}, %% comma-seperated list - {suite, undefined, "suite", string, help(suite)}, %% comma-seperated list - {group, undefined, "group", string, help(group)}, %% comma-seperated list - {testcase, undefined, "case", string, help(testcase)}, %% comma-seperated list + [{dir, undefined, "dir", string, help(dir)}, %% comma-separated list + {suite, undefined, "suite", string, help(suite)}, %% comma-separated list + {group, undefined, "group", string, help(group)}, %% comma-separated list + {testcase, undefined, "case", string, help(testcase)}, %% comma-separated list {label, undefined, "label", string, help(label)}, %% String - {config, undefined, "config", string, help(config)}, %% comma-seperated list - {spec, undefined, "spec", string, help(spec)}, %% comma-seperated list + {config, undefined, "config", string, help(config)}, %% comma-separated list + {spec, undefined, "spec", string, help(spec)}, %% comma-separated list {join_specs, undefined, "join_specs", boolean, help(join_specs)}, {allow_user_terms, undefined, "allow_user_terms", boolean, help(allow_user_terms)}, %% Bool {logdir, undefined, "logdir", string, help(logdir)}, %% dir - {logopts, undefined, "logopts", string, help(logopts)}, %% comma seperated list + {logopts, undefined, "logopts", string, help(logopts)}, %% comma-separated list {verbosity, undefined, "verbosity", integer, help(verbosity)}, %% Integer {cover, $c, "cover", {boolean, false}, help(cover)}, {repeat, undefined, "repeat", integer, help(repeat)}, %% integer @@ -736,9 +750,12 @@ ct_opts(_State) -> {name, undefined, "name", atom, help(name)}, {sname, undefined, "sname", atom, help(sname)}, {setcookie, undefined, "setcookie", atom, help(setcookie)}, - {sys_config, undefined, "sys_config", string, help(sys_config)} %% comma-seperated list + {sys_config, undefined, "sys_config", string, help(sys_config)}, %% comma-separated list + {compile_only, undefined, "compile_only", boolean, help(compile_only)} ]. +help(compile_only) -> + "Compile modules in the project with the test configuration but do not run the tests"; help(dir) -> "List of additional directories containing test suites"; help(suite) -> diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl index effc763..959ecb0 100644 --- a/src/rebar_prv_compile.erl +++ b/src/rebar_prv_compile.erl @@ -73,7 +73,7 @@ do(State) -> -spec format_error(any()) -> iolist(). format_error({missing_artifact, File}) -> - io_lib:format("Missing artifact ~s", [File]); + io_lib:format("Missing artifact ~ts", [File]); format_error(Reason) -> io_lib:format("~p", [Reason]). @@ -114,7 +114,7 @@ compile(State, AppInfo) -> compile(State, rebar_state:providers(State), AppInfo). compile(State, Providers, AppInfo) -> - ?INFO("Compiling ~s", [rebar_app_info:name(AppInfo)]), + ?INFO("Compiling ~ts", [rebar_app_info:name(AppInfo)]), AppDir = rebar_app_info:dir(AppInfo), AppInfo1 = rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, AppInfo, State), @@ -173,8 +173,8 @@ has_all_artifacts(AppInfo1) -> end. copy_app_dirs(AppInfo, OldAppDir, AppDir) -> - case ec_cnv:to_binary(filename:absname(OldAppDir)) =/= - ec_cnv:to_binary(filename:absname(AppDir)) of + case rebar_utils:to_binary(filename:absname(OldAppDir)) =/= + rebar_utils:to_binary(filename:absname(AppDir)) of true -> EbinDir = filename:join([OldAppDir, "ebin"]), %% copy all files from ebin if it exists diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl index 865c557..fca7c40 100644 --- a/src/rebar_prv_cover.erl +++ b/src/rebar_prv_cover.erl @@ -215,18 +215,18 @@ print_analysis(Analysis, true) -> format_table(Stats, CoverFiles) -> MaxLength = max(lists:foldl(fun max_length/2, 0, Stats), 20), Header = header(MaxLength), - Seperator = seperator(MaxLength), + Separator = separator(MaxLength), TotalLabel = format("total", MaxLength), TotalCov = format(calculate_total(Stats), 8), - [io_lib:format("~ts~n~ts~n~ts~n", [Seperator, Header, Seperator]), + [io_lib:format("~ts~n~ts~n~ts~n", [Separator, Header, Separator]), lists:map(fun({Mod, Coverage}) -> Name = format(Mod, MaxLength), Cov = format(Coverage, 8), io_lib:format(" | ~ts | ~ts |~n", [Name, Cov]) end, Stats), - io_lib:format("~ts~n", [Seperator]), + io_lib:format("~ts~n", [Separator]), io_lib:format(" | ~ts | ~ts |~n", [TotalLabel, TotalCov]), - io_lib:format("~ts~n", [Seperator]), + io_lib:format("~ts~n", [Separator]), io_lib:format(" coverage calculated from:~n", []), lists:map(fun(File) -> io_lib:format(" ~ts~n", [File]) @@ -242,7 +242,7 @@ max_length({ModName, _}, Min) -> header(Width) -> [" | ", format("module", Width), " | ", format("coverage", 8), " |"]. -seperator(Width) -> +separator(Width) -> [" |--", io_lib:format("~*c", [Width, $-]), "--|------------|"]. format(String, Width) -> io_lib:format("~*.ts", [Width, String]). @@ -278,7 +278,7 @@ write_index(State, Coverage) -> write_index_section(_F, []) -> ok; write_index_section(F, [{Section, DataFile, Mods}|Rest]) -> %% Write the report - ok = file:write(F, ?FMT("<h1>~s summary</h1>\n", [Section])), + ok = file:write(F, ?FMT("<h1>~ts summary</h1>\n", [Section])), ok = file:write(F, "coverage calculated from:\n<ul>"), ok = lists:foreach(fun(D) -> ok = file:write(F, io_lib:format("<li>~ts</li>", [D])) end, DataFile), @@ -303,7 +303,7 @@ strip_coverdir(File) -> 2))). cover_compile(State, apps) -> - ExclApps = [list_to_binary(A) || A <- rebar_state:get(State, cover_excl_apps, [])], + ExclApps = [rebar_utils:to_binary(A) || A <- rebar_state:get(State, cover_excl_apps, [])], Apps = filter_checkouts_and_excluded(rebar_state:project_apps(State), ExclApps), AppDirs = app_dirs(Apps), cover_compile(State, lists:filter(fun(D) -> ec_file:is_dir(D) end, AppDirs)); diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl index 9ff2bfa..a88b014 100644 --- a/src/rebar_prv_deps.erl +++ b/src/rebar_prv_deps.erl @@ -55,7 +55,7 @@ merge(Deps, SourceDeps) -> normalize(Name) when is_binary(Name) -> Name; normalize(Name) when is_atom(Name) -> - ec_cnv:to_binary(Name); + atom_to_binary(Name, unicode); normalize(Dep) when is_tuple(Dep) -> Name = element(1, Dep), setelement(1, Dep, normalize(Name)). @@ -87,31 +87,31 @@ display_deps(State, Deps) -> %% packages display_dep(_State, {Name, Vsn}) when is_list(Vsn) -> - ?CONSOLE("~s* (package ~s)", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]); + ?CONSOLE("~ts* (package ~ts)", [rebar_utils:to_binary(Name), rebar_utils:to_binary(Vsn)]); display_dep(_State, Name) when is_binary(Name) -> - ?CONSOLE("~s* (package)", [Name]); + ?CONSOLE("~ts* (package)", [Name]); display_dep(_State, {Name, Source}) when is_tuple(Source) -> - ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); + ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]); display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) -> - ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); + ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]); display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) -> - ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); + ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]); %% Locked display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) -> DepsDir = rebar_dir:deps_dir(State), - AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]), + AppDir = filename:join([DepsDir, rebar_utils:to_binary(Name)]), NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of true -> "*"; false -> "" end, - ?CONSOLE("~s~s (locked package ~s)", [Name, NeedsUpdate, Vsn]); + ?CONSOLE("~ts~ts (locked package ~ts)", [Name, NeedsUpdate, Vsn]); display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Level) -> DepsDir = rebar_dir:deps_dir(State), - AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]), + AppDir = filename:join([DepsDir, rebar_utils:to_binary(Name)]), NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of true -> "*"; false -> "" end, - ?CONSOLE("~s~s (locked ~s source)", [Name, NeedsUpdate, type(Source)]). + ?CONSOLE("~ts~ts (locked ~ts source)", [Name, NeedsUpdate, type(Source)]). type(Source) when is_tuple(Source) -> element(1, Source). diff --git a/src/rebar_prv_deps_tree.erl b/src/rebar_prv_deps_tree.erl index c0c8bab..7c6978b 100644 --- a/src/rebar_prv_deps_tree.erl +++ b/src/rebar_prv_deps_tree.erl @@ -90,7 +90,7 @@ type(Source, Verbose) when is_tuple(Source) -> {pkg, _} -> "hex package"; {Other, false} -> - io_lib:format("~s repo", [Other]); + io_lib:format("~ts repo", [Other]); {_, true} -> - io_lib:format("~s", [element(2, Source)]) + io_lib:format("~ts", [element(2, Source)]) end. diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 21d7f5a..a74eefb 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -116,18 +116,18 @@ maybe_fix_env() -> -spec format_error(any()) -> iolist(). format_error({error_processing_apps, Error}) -> - io_lib:format("Error in dialyzing apps: ~s", [Error]); + io_lib:format("Error in dialyzing apps: ~ts", [Error]); format_error({dialyzer_warnings, Warnings}) -> - io_lib:format("Warnings occured running dialyzer: ~b", [Warnings]); + io_lib:format("Warnings occurred running dialyzer: ~b", [Warnings]); format_error({unknown_application, App}) -> - io_lib:format("Could not find application: ~s", [App]); + io_lib:format("Could not find application: ~ts", [App]); format_error({unknown_module, Mod}) -> - io_lib:format("Could not find module: ~s", [Mod]); + io_lib:format("Could not find module: ~ts", [Mod]); format_error({duplicate_module, Mod, File1, File2}) -> - io_lib:format("Duplicates of module ~s: ~s ~s", [Mod, File1, File2]); + io_lib:format("Duplicates of module ~ts: ~ts ~ts", [Mod, File1, File2]); format_error({output_file_error, File, Error}) -> Error1 = file:format_error(Error), - io_lib:format("Failed to write to ~s: ~s", [File, Error1]); + io_lib:format("Failed to write to ~ts: ~ts", [File, Error1]); format_error(Reason) -> io_lib:format("~p", [Reason]). @@ -155,7 +155,7 @@ do(State, Plt) -> 0 -> {ok, State2}; TotalWarnings -> - ?INFO("Warnings written to ~s", [Output]), + ?INFO("Warnings written to ~ts", [Output]), throw({dialyzer_warnings, TotalWarnings}) end. @@ -229,7 +229,7 @@ apps_files([AppName | DepApps], SkipApps, Files) -> apps_files(DepApps, SkipApps, Files); false -> AppFiles = app_files(AppName), - ?DEBUG("~s modules: ~p", [AppName, dict:fetch_keys(AppFiles)]), + ?DEBUG("~ts modules: ~p", [AppName, dict:fetch_keys(AppFiles)]), Files2 = merge_files(Files, AppFiles), apps_files(DepApps, [AppName | SkipApps], Files2) end. @@ -505,7 +505,7 @@ format_warnings(Opts, Output, Warnings) -> length(Warnings). console_warnings(Warnings) -> - _ = [?CONSOLE("~s", [Warning]) || Warning <- Warnings], + _ = [?CONSOLE("~ts", [Warning]) || Warning <- Warnings], ok. file_warnings(_, []) -> diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl index 97f70a9..9517335 100644 --- a/src/rebar_prv_edoc.erl +++ b/src/rebar_prv_edoc.erl @@ -42,8 +42,8 @@ do(State) -> Res = try lists:foldl(fun(AppInfo, EdocOptsAcc) -> rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State), - AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)), - ?INFO("Running edoc for ~s", [AppName]), + AppName = rebar_utils:to_list(rebar_app_info:name(AppInfo)), + ?INFO("Running edoc for ~ts", [AppName]), AppDir = rebar_app_info:dir(AppInfo), AppRes = (catch edoc:application(list_to_atom(AppName), AppDir, EdocOptsAcc)), rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State), @@ -74,7 +74,7 @@ do(State) -> -spec format_error(any()) -> iolist(). format_error({app_failed, AppName}) -> - io_lib:format("Failed to generate documentation for app '~s'", [AppName]); + io_lib:format("Failed to generate documentation for app '~ts'", [AppName]); format_error(Reason) -> io_lib:format("~p", [Reason]). @@ -89,4 +89,4 @@ add_to_paths([], Path) -> add_to_paths([{doc_path, Paths}|T], Path) -> [{doc_path, [Path | Paths]} | T]; add_to_paths([H|T], Path) -> - [H | add_to_paths(Path, T)]. + [H | add_to_paths(T, Path)]. diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl index 7ee20c2..cffbbdc 100644 --- a/src/rebar_prv_escriptize.erl +++ b/src/rebar_prv_escriptize.erl @@ -75,7 +75,7 @@ do(State) -> end; Name -> AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State), - case rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps) of + case rebar_app_utils:find(rebar_utils:to_binary(Name), AllApps) of {ok, AppInfo} -> escriptize(State, AppInfo); _ -> @@ -87,12 +87,12 @@ do(State) -> escriptize(State0, App) -> AppName = rebar_app_info:name(App), - AppNameStr = ec_cnv:to_list(AppName), + AppNameStr = rebar_utils:to_list(AppName), %% Get the output filename for the escript -- this may include dirs Filename = filename:join([rebar_dir:base_dir(State0), "bin", rebar_state:get(State0, escript_name, AppName)]), - ?DEBUG("Creating escript file ~s", [Filename]), + ?DEBUG("Creating escript file ~ts", [Filename]), ok = filelib:ensure_dir(Filename), State = rebar_state:escript_path(State0, Filename), @@ -116,7 +116,7 @@ escriptize(State0, App) -> ExtraFiles = usort(InclBeams ++ InclExtra), Files = get_nonempty(EbinFiles ++ ExtraFiles), - DefaultEmuArgs = ?FMT("%%! -escript main ~s -pz ~s/~s/ebin\n", + DefaultEmuArgs = ?FMT("%%! -escript main ~ts -pz ~ts/~ts/ebin\n", [AppNameStr, AppNameStr, AppNameStr]), EscriptSections = [ {shebang, @@ -130,9 +130,15 @@ escriptize(State0, App) -> throw(?PRV_ERROR({escript_creation_failed, AppName, EscriptError})) end, - %% Finally, update executable perms for our script - {ok, #file_info{mode = Mode}} = file:read_file_info(Filename), - ok = file:change_mode(Filename, Mode bor 8#00111), + %% Finally, update executable perms for our script on *nix or write out + %% script files on win32 + case os:type() of + {unix, _} -> + {ok, #file_info{mode = Mode}} = file:read_file_info(Filename), + ok = file:change_mode(Filename, Mode bor 8#00111); + {win32, _} -> + write_windows_script(Filename) + end, {ok, State}. -spec format_error(any()) -> iolist(). @@ -157,7 +163,7 @@ get_apps_beams(Apps, AllApps) -> get_apps_beams([], _, Acc) -> Acc; get_apps_beams([App | Rest], AllApps, Acc) -> - case rebar_app_utils:find(ec_cnv:to_binary(App), AllApps) of + case rebar_app_utils:find(rebar_utils:to_binary(App), AllApps) of {ok, App1} -> OutDir = filename:absname(rebar_app_info:ebin_dir(App1)), Beams = get_app_beams(App, OutDir), @@ -229,7 +235,7 @@ get_nonempty(Files) -> [{FName,FBin} || {FName,FBin} <- Files, FBin =/= <<>>]. find_deps(AppNames, AllApps) -> - BinAppNames = [ec_cnv:to_binary(Name) || Name <- AppNames], + BinAppNames = [rebar_utils:to_binary(Name) || Name <- AppNames], [ec_cnv:to_atom(Name) || Name <- find_deps_of_deps(BinAppNames, AllApps, BinAppNames)]. @@ -239,7 +245,7 @@ find_deps_of_deps([Name|Names], Apps, Acc) -> ?DEBUG("processing ~p", [Name]), {ok, App} = rebar_app_utils:find(Name, Apps), DepNames = proplists:get_value(applications, rebar_app_info:app_details(App), []), - BinDepNames = [ec_cnv:to_binary(Dep) || Dep <- DepNames, + BinDepNames = [rebar_utils:to_binary(Dep) || Dep <- DepNames, %% ignore system libs; shouldn't include them. DepDir <- [code:lib_dir(Dep)], DepDir =:= {error, bad_name} orelse % those are all local @@ -258,3 +264,14 @@ def(Rm, State, Key, Default) -> rm_newline(String) -> [C || C <- String, C =/= $\n]. + +write_windows_script(Target) -> + CmdPath = if is_binary(Target) -> <<Target/binary, ".cmd">>; + is_list(Target) -> Target ++ ".cmd" + end, + CmdScript= + "@echo off\r\n" + "setlocal\r\n" + "set rebarscript=%~f0\r\n" + "escript.exe \"%rebarscript:.cmd=%\" %*\r\n", + ok = file:write_file(CmdPath, CmdScript). diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 7d44137..65addc3 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -83,13 +83,16 @@ run_tests(State, Tests) -> EUnitOpts = resolve_eunit_opts(State), ?DEBUG("eunit_tests ~p", [T]), ?DEBUG("eunit_opts ~p", [EUnitOpts]), - Result = eunit:test(T, EUnitOpts), - ok = maybe_write_coverdata(State), - case handle_results(Result) of - {error, Reason} -> - ?PRV_ERROR(Reason); - ok -> - {ok, State} + try eunit:test(T, EUnitOpts) of + Result -> + ok = maybe_write_coverdata(State), + case handle_results(Result) of + {error, Reason} -> + ?PRV_ERROR(Reason); + ok -> + {ok, State} + end + catch error:badarg -> ?PRV_ERROR({error, badarg}) end. -spec format_error(any()) -> iolist(). diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index c9fe0ad..65aabff 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -104,22 +104,22 @@ do_(State) -> %% @doc convert a given exception's payload into an io description. -spec format_error(any()) -> iolist(). format_error({dep_app_not_found, AppDir, AppName}) -> - io_lib:format("Dependency failure: Application ~s not found at the top level of directory ~s", [AppName, AppDir]); + io_lib:format("Dependency failure: Application ~ts not found at the top level of directory ~ts", [AppName, AppDir]); format_error({load_registry_fail, Dep}) -> - io_lib:format("Error loading registry to resolve version of ~s. Try fixing by running 'rebar3 update'", [Dep]); + io_lib:format("Error loading registry to resolve version of ~ts. Try fixing by running 'rebar3 update'", [Dep]); format_error({bad_constraint, Name, Constraint}) -> - io_lib:format("Unable to parse version for package ~s: ~s", [Name, Constraint]); + io_lib:format("Unable to parse version for package ~ts: ~ts", [Name, Constraint]); format_error({parse_dep, Dep}) -> io_lib:format("Failed parsing dep ~p", [Dep]); format_error({not_rebar_package, Package, Version}) -> - io_lib:format("Package not buildable with rebar3: ~s-~s", [Package, Version]); + io_lib:format("Package not buildable with rebar3: ~ts-~ts", [Package, Version]); format_error({missing_package, Package, Version}) -> - io_lib:format("Package not found in registry: ~s-~s", [Package, Version]); + io_lib:format("Package not found in registry: ~ts-~ts", [Package, Version]); format_error({missing_package, Package}) -> - io_lib:format("Package not found in registry: ~s", [Package]); + io_lib:format("Package not found in registry: ~ts", [Package]); format_error({cycles, Cycles}) -> Prints = [["applications: ", - [io_lib:format("~s ", [Dep]) || Dep <- Cycle], + [io_lib:format("~ts ", [Dep]) || Dep <- Cycle], "depend on each other\n"] || Cycle <- Cycles], ["Dependency cycle(s) detected:\n", Prints]; @@ -140,7 +140,9 @@ handle_deps_as_profile(Profile, State, Deps, Upgrade) -> DepsDir = profile_dep_dir(State, Profile), Deps1 = rebar_app_utils:parse_deps(DepsDir, Deps, State, Locks, Level), ProfileLevelDeps = [{Profile, Deps1, Level}], - handle_profile_level(ProfileLevelDeps, [], sets:new(), Upgrade, Locks, State). + RootSeen = sets:from_list([rebar_app_info:name(AppInfo) + || AppInfo <- rebar_state:project_apps(State)]), + handle_profile_level(ProfileLevelDeps, [], RootSeen, RootSeen, Upgrade, Locks, State). %% =================================================================== %% Internal functions @@ -153,7 +155,9 @@ deps_per_profile(Profiles, Upgrade, State) -> Deps = lists:foldl(fun(Profile, DepAcc) -> [parsed_profile_deps(State, Profile, Level) | DepAcc] end, [], Profiles), - handle_profile_level(Deps, [], sets:new(), Upgrade, Locks, State). + RootSeen = sets:from_list([rebar_app_info:name(AppInfo) + || AppInfo <- rebar_state:project_apps(State)]), + handle_profile_level(Deps, [], RootSeen, RootSeen, Upgrade, Locks, State). parsed_profile_deps(State, Profile, Level) -> ParsedDeps = rebar_state:get(State, {parsed_deps, Profile}, []), @@ -162,17 +166,27 @@ parsed_profile_deps(State, Profile, Level) -> %% 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([], Apps, _Seen, _Upgrade, _Locks, State) -> +%% +%% There are two 'seen' sets: one for the top-level apps (`RootSeen') and +%% one for all dependencies (`Seen'). The former is used to know when +%% to skip the resolving of dependencies altogether (since they're already +%% top-level apps), while the latter is used to prevent reprocessing +%% deps more than one. +handle_profile_level([], Apps, _RootSeen, _Seen, _Upgrade, _Locks, State) -> {Apps, State}; -handle_profile_level([{Profile, Deps, Level} | Rest], Apps, Seen, Upgrade, Locks, State) -> +handle_profile_level([{Profile, Deps, Level} | Rest], Apps, RootSeen, Seen, Upgrade, Locks, State) -> + Deps0 = [rebar_app_utils:expand_deps_sources(Dep, State) + || Dep <- Deps, + %% skip top-level apps being double-declared + not sets:is_element(rebar_app_info:name(Dep), RootSeen)], {Deps1, Apps1, State1, Seen1} = - update_deps(Profile, Level, Deps, Apps + update_deps(Profile, Level, Deps0, Apps ,State, Upgrade, Seen, Locks), Deps2 = case Deps1 of [] -> Rest; _ -> Rest ++ [{Profile, Deps1, Level+1}] end, - handle_profile_level(Deps2, Apps1, sets:union(Seen, Seen1), Upgrade, Locks, State1). + handle_profile_level(Deps2, Apps1, RootSeen, sets:union(Seen, Seen1), Upgrade, Locks, State1). find_cycles(Apps) -> case rebar_digraph:compile_order(Apps) of @@ -291,7 +305,7 @@ handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level) -> -spec maybe_fetch(rebar_app_info:t(), atom(), boolean(), sets:set(binary()), rebar_state:t()) -> {boolean(), rebar_app_info:t()}. maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) -> - AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)), + AppDir = rebar_utils:to_list(rebar_app_info:dir(AppInfo)), %% Don't fetch dep if it exists in the _checkouts dir case rebar_app_info:is_checkout(AppInfo) of true -> @@ -346,7 +360,7 @@ symlink_dep(State, From, To) -> ok -> RelativeFrom = make_relative_to_root(State, From), RelativeTo = make_relative_to_root(State, To), - ?INFO("Linking ~s to ~s", [RelativeFrom, RelativeTo]), + ?INFO("Linking ~ts to ~ts", [RelativeFrom, RelativeTo]), ok; exists -> ok @@ -359,7 +373,7 @@ make_relative_to_root(State, Path) when is_list(Path) -> rebar_dir:make_relative_path(Path, Root). fetch_app(AppInfo, AppDir, State) -> - ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), + ?INFO("Fetching ~ts (~p)", [rebar_app_info:name(AppInfo), format_source(rebar_app_info:source(AppInfo))]), Source = rebar_app_info:source(AppInfo), true = rebar_fetch:download_source(AppDir, Source, State). @@ -384,12 +398,12 @@ maybe_upgrade(AppInfo, AppDir, Upgrade, State) -> true -> case rebar_fetch:needs_update(AppDir, Source, State) of true -> - ?INFO("Upgrading ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]), + ?INFO("Upgrading ~ts (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]), true = rebar_fetch:download_source(AppDir, Source, State); false -> case Upgrade of true -> - ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]), + ?INFO("No upgrade needed for ~ts", [rebar_app_info:name(AppInfo)]), false; false -> false @@ -400,7 +414,7 @@ maybe_upgrade(AppInfo, AppDir, Upgrade, State) -> end. warn_skip_deps(AppInfo, State) -> - Msg = "Skipping ~s (from ~p) as an app of the same name " + Msg = "Skipping ~ts (from ~p) as an app of the same name " "has already been fetched", Args = [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)], diff --git a/src/rebar_prv_local_install.erl b/src/rebar_prv_local_install.erl index 1b58859..282c548 100644 --- a/src/rebar_prv_local_install.erl +++ b/src/rebar_prv_local_install.erl @@ -60,7 +60,7 @@ format_error(Reason) -> bin_contents(OutputDir) -> <<"#!/usr/bin/env sh -erl -pz ", (ec_cnv:to_binary(OutputDir))/binary,"/*/ebin +sbtu +A0 -noshell -boot start_clean -s rebar3 main $REBAR3_ERL_ARGS -extra \"$@\" +erl -pz ", (rebar_utils:to_binary(OutputDir))/binary,"/*/ebin +sbtu +A0 -noshell -boot start_clean -s rebar3 main $REBAR3_ERL_ARGS -extra \"$@\" ">>. extract_escript(State, ScriptPath) -> @@ -73,7 +73,7 @@ extract_escript(State, ScriptPath) -> OutputDir = filename:join(rebar_dir:global_cache_dir(Opts), "lib"), filelib:ensure_dir(filename:join(OutputDir, "empty")), - ?INFO("Extracting rebar3 libs to ~s...", [OutputDir]), + ?INFO("Extracting rebar3 libs to ~ts...", [OutputDir]), zip:extract(Archive, [{cwd, OutputDir}]), BinDir = filename:join(rebar_dir:global_cache_dir(Opts), "bin"), @@ -84,12 +84,12 @@ extract_escript(State, ScriptPath) -> uid = Uid, gid = Gid}} = file:read_file_info(ScriptPath), - ?INFO("Writing rebar3 run script ~s...", [BinFile]), + ?INFO("Writing rebar3 run script ~ts...", [BinFile]), file:write_file(BinFile, bin_contents(OutputDir)), ok = file:write_file_info(BinFile, #file_info{mode=33277, uid=Uid, gid=Gid}), - ?INFO("Add to $PATH for use: export PATH=$PATH:~s", [BinDir]), + ?INFO("Add to $PATH for use: export PATH=~ts:$PATH", [BinDir]), {ok, State}. diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl index 152a56e..f632362 100644 --- a/src/rebar_prv_new.erl +++ b/src/rebar_prv_new.erl @@ -60,7 +60,7 @@ do(State) -> -spec format_error(any()) -> iolist(). format_error({consult, File, Reason}) -> - io_lib:format("Error consulting file at ~s for reason ~p", [File, Reason]); + io_lib:format("Error consulting file at ~ts for reason ~p", [File, Reason]); format_error(Reason) -> io_lib:format("~p", [Reason]). @@ -70,7 +70,7 @@ format_error(Reason) -> list_templates(State) -> lists:foldl(fun({error, {consult, File, Reason}}, Acc) -> - ?WARN("Error consulting template file ~s for reason ~p", + ?WARN("Error consulting template file ~ts for reason ~p", [File, Reason]), Acc ; (Tpl, Acc) -> @@ -116,16 +116,16 @@ show_short_templates(List) -> lists:map(fun show_short_template/1, lists:sort(List)). show_short_template({Name, Type, _Location, Description, _Vars}) -> - io:format("~s (~s): ~s~n", + io:format("~ts (~ts): ~ts~n", [Name, format_type(Type), format_description(Description)]). show_template({Name, Type, Location, Description, Vars}) -> - io:format("~s:~n" - "\t~s~n" - "\tDescription: ~s~n" - "\tVariables:~n~s~n", + io:format("~ts:~n" + "\t~ts~n" + "\tDescription: ~ts~n" + "\tVariables:~n~ts~n", [Name, format_type(Type, Location), format_description(Description), @@ -141,9 +141,9 @@ format_type(escript, _) -> format_type(builtin, _) -> "built-in template"; format_type(plugin, Loc) -> - io_lib:format("plugin template (~s)", [Loc]); + io_lib:format("plugin template (~ts)", [Loc]); format_type(file, Loc) -> - io_lib:format("custom template (~s)", [Loc]). + io_lib:format("custom template (~ts)", [Loc]). format_description(Description) -> case Description of @@ -156,4 +156,4 @@ format_vars(Vars) -> [format_var(Var) || Var <- Vars]. format_var({Var, Default}) -> io_lib:format("\t\t~p=~p~n",[Var, Default]); format_var({Var, Default, Doc}) -> - io_lib:format("\t\t~p=~p (~s)~n", [Var, Default, Doc]). + io_lib:format("\t\t~p=~p (~ts)~n", [Var, Default, Doc]). diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl index 7217ab8..6e8e683 100644 --- a/src/rebar_prv_packages.erl +++ b/src/rebar_prv_packages.erl @@ -30,7 +30,7 @@ do(State) -> rebar_packages:packages(State), case rebar_state:command_args(State) of [Name] -> - print_packages(get_packages(iolist_to_binary(Name))); + print_packages(get_packages(rebar_utils:to_binary(Name))); _ -> print_packages(sort_packages()) end, @@ -47,7 +47,7 @@ print_packages(Pkgs) -> ,ec_semver:parse(B)) end, Vsns), VsnStr = join(SortedVsns, <<", ">>), - ?CONSOLE("~s:~n Versions: ~s~n", [Name, VsnStr]) + ?CONSOLE("~ts:~n Versions: ~ts~n", [Name, VsnStr]) end, Pkgs). sort_packages() -> @@ -71,4 +71,4 @@ join([Bin | T], Sep) -> info(Description) -> - io_lib:format("~s.~n", [Description]). + io_lib:format("~ts.~n", [Description]). diff --git a/src/rebar_prv_path.erl b/src/rebar_prv_path.erl index 4259eec..75d38eb 100644 --- a/src/rebar_prv_path.erl +++ b/src/rebar_prv_path.erl @@ -27,7 +27,7 @@ init(State) -> {example, "rebar3 path"}, {short_desc, "Print paths to build dirs in current profile."}, {desc, "Print paths to build dirs in current profile."}, - {opts, eunit_opts(State)}])), + {opts, path_opts(State)}])), {ok, State1}. @@ -75,23 +75,23 @@ paths([{src, true}|Rest], Apps, State, Acc) -> paths([{rel, true}|Rest], Apps, State, Acc) -> paths(Rest, Apps, State, [rel_dir(State)|Acc]). -base_dir(State) -> io_lib:format("~s", [rebar_dir:base_dir(State)]). -bin_dir(State) -> io_lib:format("~s/bin", [rebar_dir:base_dir(State)]). -lib_dir(State) -> io_lib:format("~s", [rebar_dir:deps_dir(State)]). -rel_dir(State) -> io_lib:format("~s/rel", [rebar_dir:base_dir(State)]). +base_dir(State) -> io_lib:format("~ts", [rebar_dir:base_dir(State)]). +bin_dir(State) -> io_lib:format("~ts/bin", [rebar_dir:base_dir(State)]). +lib_dir(State) -> io_lib:format("~ts", [rebar_dir:deps_dir(State)]). +rel_dir(State) -> io_lib:format("~ts/rel", [rebar_dir:base_dir(State)]). ebin_dirs(Apps, State) -> - lists:map(fun(App) -> io_lib:format("~s/~s/ebin", [rebar_dir:deps_dir(State), App]) end, Apps). + lists:map(fun(App) -> io_lib:format("~ts/~ts/ebin", [rebar_dir:deps_dir(State), App]) end, Apps). priv_dirs(Apps, State) -> - lists:map(fun(App) -> io_lib:format("~s/~s/priv", [rebar_dir:deps_dir(State), App]) end, Apps). + lists:map(fun(App) -> io_lib:format("~ts/~ts/priv", [rebar_dir:deps_dir(State), App]) end, Apps). src_dirs(Apps, State) -> - lists:map(fun(App) -> io_lib:format("~s/~s/src", [rebar_dir:deps_dir(State), App]) end, Apps). + lists:map(fun(App) -> io_lib:format("~ts/~ts/src", [rebar_dir:deps_dir(State), App]) end, Apps). print_paths_if_exist(Paths, State) -> {RawOpts, _} = rebar_state:command_parsed_args(State), Sep = proplists:get_value(separator, RawOpts, " "), RealPaths = lists:filter(fun(P) -> ec_file:is_dir(P) end, Paths), - io:format("~s", [string:join(RealPaths, Sep)]). + io:format("~ts", [string:join(RealPaths, Sep)]). project_deps(State) -> Profiles = rebar_state:current_profiles(State), @@ -107,7 +107,7 @@ normalize(AppName) when is_list(AppName) -> AppName; normalize(AppName) when is_atom(AppName) -> atom_to_list(AppName); normalize(AppName) when is_binary(AppName) -> binary_to_list(AppName). -eunit_opts(_State) -> +path_opts(_State) -> [{app, undefined, "app", string, help(app)}, {base, undefined, "base", boolean, help(base)}, {bin, undefined, "bin", boolean, help(bin)}, @@ -118,7 +118,7 @@ eunit_opts(_State) -> {src, undefined, "src", boolean, help(src)}, {rel, undefined, "rel", boolean, help(rel)}]. -help(app) -> "Comma seperated list of applications to return paths for."; +help(app) -> "Comma separated list of applications to return paths for."; help(base) -> "Return the `base' path of the current profile."; help(bin) -> "Return the `bin' path of the current profile."; help(ebin) -> "Return all `ebin' paths of the current profile's applications."; diff --git a/src/rebar_prv_plugins.erl b/src/rebar_prv_plugins.erl index 7e6b88e..4bea3b3 100644 --- a/src/rebar_prv_plugins.erl +++ b/src/rebar_prv_plugins.erl @@ -34,14 +34,17 @@ do(State) -> GlobalConfigFile = rebar_dir:global_config(), GlobalConfig = rebar_state:new(rebar_config:consult_file(GlobalConfigFile)), GlobalPlugins = rebar_state:get(GlobalConfig, plugins, []), + GlobalSrcDirs = rebar_state:get(GlobalConfig, src_dirs, ["src"]), GlobalPluginsDir = filename:join([rebar_dir:global_cache_dir(rebar_state:opts(State)), "plugins", "*"]), - GlobalApps = rebar_app_discover:find_apps([GlobalPluginsDir], all), + GlobalApps = rebar_app_discover:find_apps([GlobalPluginsDir], GlobalSrcDirs, all), display_plugins("Global plugins", GlobalApps, GlobalPlugins), + RebarOpts = rebar_state:opts(State), + SrcDirs = rebar_dir:src_dirs(RebarOpts, ["src"]), Plugins = rebar_state:get(State, plugins, []), - PluginsDir = filename:join(rebar_dir:plugins_dir(State), "*"), - CheckoutsDir = filename:join(rebar_dir:checkouts_dir(State), "*"), - Apps = rebar_app_discover:find_apps([CheckoutsDir, PluginsDir], all), + PluginsDirs = filelib:wildcard(filename:join(rebar_dir:plugins_dir(State), "*")), + CheckoutsDirs = filelib:wildcard(filename:join(rebar_dir:checkouts_dir(State), "*")), + Apps = rebar_app_discover:find_apps(CheckoutsDirs++PluginsDirs, SrcDirs, all), display_plugins("Local plugins", Apps, Plugins), {ok, State}. @@ -52,19 +55,19 @@ format_error(Reason) -> display_plugins(_Header, _Apps, []) -> ok; display_plugins(Header, Apps, Plugins) -> - ?CONSOLE("--- ~s ---", [Header]), + ?CONSOLE("--- ~ts ---", [Header]), display_plugins(Apps, Plugins), ?CONSOLE("", []). display_plugins(Apps, Plugins) -> lists:foreach(fun(Plugin) -> - Name = if is_atom(Plugin) -> ec_cnv:to_binary(Plugin); - is_tuple(Plugin) -> ec_cnv:to_binary(element(1, Plugin)) + Name = if is_atom(Plugin) -> atom_to_binary(Plugin, unicode); + is_tuple(Plugin) -> rebar_utils:to_binary(element(1, Plugin)) end, case rebar_app_utils:find(Name, Apps) of {ok, _App} -> - ?CONSOLE("~s", [Name]); + ?CONSOLE("~ts", [Name]); error -> - ?DEBUG("Unable to find plugin ~s", [Name]) + ?DEBUG("Unable to find plugin ~ts", [Name]) end end, Plugins). diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl index 03521c7..7420c83 100644 --- a/src/rebar_prv_plugins_upgrade.erl +++ b/src/rebar_prv_plugins_upgrade.erl @@ -44,7 +44,7 @@ do(State) -> format_error(no_plugin_arg) -> io_lib:format("Must give an installed plugin to upgrade as an argument", []); format_error({not_found, Plugin}) -> - io_lib:format("Plugin to upgrade not found: ~s", [Plugin]); + io_lib:format("Plugin to upgrade not found: ~ts", [Plugin]); format_error(Reason) -> io_lib:format("~p", [Reason]). diff --git a/src/rebar_prv_report.erl b/src/rebar_prv_report.erl index d6c8b60..73e9624 100644 --- a/src/rebar_prv_report.erl +++ b/src/rebar_prv_report.erl @@ -44,7 +44,7 @@ do(State) -> {ok, Vsn} = application:get_key(rebar, vsn), {ok, Apps} = application:get_key(rebar, applications), [application:load(App) || App <- Apps], - Vsns = [io_lib:format("~p: ~s~n", [App, AVsn]) + Vsns = [io_lib:format("~p: ~ts~n", [App, AVsn]) || App <- lists:sort(Apps), {ok, AVsn} <- [application:get_key(App, vsn)]], %% Show OS and versions @@ -59,10 +59,10 @@ do(State) -> %% ?CONSOLE( "Rebar3 report~n" - " version ~s~n" - " generated at ~s~n" + " version ~ts~n" + " generated at ~ts~n" "=================~n" - "Please submit this along with your issue at ~s " + "Please submit this along with your issue at ~ts " "(and feel free to edit out private information, if any)~n" "-----------------~n" "Task: ~ts~n" @@ -75,11 +75,11 @@ do(State) -> "Library directory: ~ts~n" "-----------------~n" "Loaded Applications:~n" - "~s~n" + "~ts~n" "-----------------~n" "Escript path: ~ts~n" "Providers:~n" - " ~s", + " ~ts", [Vsn, time_to_string(UTC), ?ISSUES_URL, Command, Task, OS, ERTS, Root, Lib, @@ -100,4 +100,4 @@ time_to_string({{Y,M,D},{H,Min,S}}) -> [Y,M,D,H,Min,S])). parse_task(Str) -> - hd(re:split(Str, " ")). + hd(re:split(Str, " ", [unicode])). diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index c1bf735..c958dde 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -360,8 +360,10 @@ boot_apps(Apps) -> ok. normalize_load_apps([]) -> []; +normalize_load_apps([{_App, none} | T]) -> normalize_load_apps(T); normalize_load_apps([{App, _} | T]) -> [App | normalize_load_apps(T)]; normalize_load_apps([{App, _Vsn, load} | T]) -> [App | normalize_load_apps(T)]; +normalize_load_apps([{_App, _Vsn, none} | T]) -> normalize_load_apps(T); normalize_load_apps([{App, _Vsn, Operator} | T]) when is_atom(Operator) -> [App | normalize_load_apps(T)]; normalize_load_apps([App | T]) when is_atom(App) -> [App | normalize_load_apps(T)]. @@ -369,12 +371,13 @@ normalize_load_apps([App | T]) when is_atom(App) -> [App | normalize_load_apps(T normalize_boot_apps([]) -> []; normalize_boot_apps([{_App, load} | T]) -> normalize_boot_apps(T); normalize_boot_apps([{_App, _Vsn, load} | T]) -> normalize_boot_apps(T); +normalize_boot_apps([{_App, none} | T]) -> normalize_boot_apps(T); +normalize_boot_apps([{_App, _Vsn, none} | T]) -> normalize_boot_apps(T); normalize_boot_apps([{App, _Vsn, Operator} | T]) when is_atom(Operator) -> [App | normalize_boot_apps(T)]; normalize_boot_apps([{App, _Vsn} | T]) -> [App | normalize_boot_apps(T)]; normalize_boot_apps([App | T]) when is_atom(App) -> [App | normalize_boot_apps(T)]. - remove_error_handler(0) -> ?WARN("Unable to remove simple error_logger handler", []); remove_error_handler(N) -> diff --git a/src/rebar_prv_unlock.erl b/src/rebar_prv_unlock.erl index 7ff0d89..51c57ab 100644 --- a/src/rebar_prv_unlock.erl +++ b/src/rebar_prv_unlock.erl @@ -66,7 +66,7 @@ format_error(Reason) -> handle_unlocks(State, Locks, LockFile) -> {Args, _} = rebar_state:command_parsed_args(State), - Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>))), + Names = parse_names(rebar_utils:to_binary(proplists:get_value(package, Args, <<"">>))), case [Lock || Lock = {Name, _, _} <- Locks, not lists:member(Name, Names)] of [] -> file:delete(LockFile); @@ -77,7 +77,7 @@ handle_unlocks(State, Locks, LockFile) -> end. parse_names(Bin) -> - case lists:usort(re:split(Bin, <<" *, *">>, [trim])) of + case lists:usort(re:split(Bin, <<" *, *">>, [trim, unicode])) of [<<"">>] -> []; % nothing submitted Other -> Other end. diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 046c864..a019c5a 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -150,7 +150,7 @@ update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) -> %% and doubled since spaces seem not to be %% enforced {false, Vsn} -> - ?WARN("[~s:~s], Bad dependency version for ~s: ~s.", + ?WARN("[~ts:~ts], Bad dependency version for ~ts: ~ts.", [Pkg, PkgVsn, Dep, Vsn]), DepsListAcc; {_, <<"~>", Vsn/binary>>} -> @@ -202,14 +202,14 @@ valid_vsn(Vsn) -> SemVerRegExp = "v?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?" "(-[0-9a-z-]+(\\.[0-9a-z-]+)*)?(\\+[0-9a-z-]+(\\.[0-9a-z-]+)*)?", SupportedVersions = "^(>=?|<=?|~>|==)?\\s*" ++ SemVerRegExp ++ "$", - re:run(Vsn, SupportedVersions) =/= nomatch. + re:run(Vsn, SupportedVersions, [unicode]) =/= nomatch. highest_matching({Pkg, PkgVsn, Dep, App}, Vsn, HexRegistry, State, DepsListAcc) -> case rebar_packages:find_highest_matching(Pkg, PkgVsn, Dep, Vsn, HexRegistry, State) of {ok, HighestDepVsn} -> [{App, {pkg, Dep, HighestDepVsn, undefined}} | DepsListAcc]; none -> - ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", + ?WARN("[~ts:~ts] Missing registry entry for package ~ts. Try to fix with `rebar3 update`", [Pkg, PkgVsn, Dep]), DepsListAcc end. @@ -220,7 +220,7 @@ cmp({_Pkg, _PkgVsn, Dep, _App} = Dep1, Vsn, HexRegistry, State, DepsListAcc, Cmp cmp_(undefined, _MinVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep, _App}, _CmpFun) -> - ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", + ?WARN("[~ts:~ts] Missing registry entry for package ~ts. Try to fix with `rebar3 update`", [Pkg, PkgVsn, Dep]), DepsListAcc; cmp_(HighestDepVsn, _MinVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep, App}, _CmpFun) -> @@ -241,7 +241,7 @@ cmpl({_Pkg, _PkgVsn, Dep, _App} = Dep1, Vsn, HexRegistry, State, DepsListAcc, Cm cmpl_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun). cmpl_(undefined, _MaxVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep, _App}, _CmpFun) -> - ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", + ?WARN("[~ts:~ts] Missing registry entry for package ~ts. Try to fix with `rebar3 update`", [Pkg, PkgVsn, Dep]), DepsListAcc; diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl index 18c307b..5a7dff8 100644 --- a/src/rebar_prv_upgrade.erl +++ b/src/rebar_prv_upgrade.erl @@ -32,7 +32,7 @@ init(State) -> {deps, ?DEPS}, {example, "rebar3 upgrade [cowboy[,ranch]]"}, {short_desc, "Upgrade dependencies."}, - {desc, "Upgrade project dependecies. Mentioning no application " + {desc, "Upgrade project dependencies. Mentioning no application " "will upgrade all dependencies. To upgrade specific dependencies, " "their names can be listed in the command."}, {opts, [ @@ -68,9 +68,11 @@ do(State) -> ProfileDeps = rebar_state:get(State, {deps, default}, []), Deps = [Dep || Dep <- TopDeps ++ ProfileDeps, % TopDeps > ProfileDeps is_atom(Dep) orelse is_atom(element(1, Dep))], - Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>)), Locks), + Names = parse_names(rebar_utils:to_binary(proplists:get_value(package, Args, <<"">>)), Locks), DepsDict = deps_dict(rebar_state:all_deps(State)), - case prepare_locks(Names, Deps, Locks, [], DepsDict) of + AltDeps = find_non_default_deps(Deps, State), + FilteredNames = cull_default_names_if_profiles(Names, Deps, State), + case prepare_locks(FilteredNames, Deps, Locks, [], DepsDict, AltDeps) of {error, Reason} -> {error, Reason}; {Locks0, _Unlocks0} -> @@ -107,7 +109,7 @@ format_error(Reason) -> io_lib:format("~p", [Reason]). parse_names(Bin, Locks) -> - case lists:usort(re:split(Bin, <<" *, *">>, [trim])) of + case lists:usort(re:split(Bin, <<" *, *">>, [trim, unicode])) of %% Nothing submitted, use *all* apps [<<"">>] -> [Name || {Name, _, 0} <- Locks]; [] -> [Name || {Name, _, 0} <- Locks]; @@ -115,20 +117,45 @@ parse_names(Bin, Locks) -> Other -> Other end. -prepare_locks([], _, Locks, Unlocks, _Dict) -> +%% Find alternative deps in non-default profiles since they may +%% need to be passed through (they are never locked) +find_non_default_deps(Deps, State) -> + AltProfiles = rebar_state:current_profiles(State) -- [default], + AltProfileDeps = lists:append([ + rebar_state:get(State, {deps, Profile}, []) || Profile <- AltProfiles] + ), + [Dep || Dep <- AltProfileDeps, + is_atom(Dep) orelse is_atom(element(1, Dep)) + andalso not lists:member(Dep, Deps)]. + +%% If any alt profiles are used, remove the default profiles from +%% the upgrade list and warn about it. +cull_default_names_if_profiles(Names, Deps, State) -> + case rebar_state:current_profiles(State) of + [default] -> + Names; + _ -> + ?INFO("Dependencies in the default profile will not be upgraded", []), + lists:filter(fun(Name) -> + AtomName = binary_to_atom(Name, utf8), + rebar_utils:tup_find(AtomName, Deps) == false + end, Names) + end. + +prepare_locks([], _, Locks, Unlocks, _Dict, _AltDeps) -> {Locks, Unlocks}; -prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict) -> +prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict, AltDeps) -> AtomName = binary_to_atom(Name, utf8), case lists:keyfind(Name, 1, Locks) of {_, _, 0} = Lock -> case rebar_utils:tup_find(AtomName, Deps) of false -> - ?WARN("Dependency ~s has been removed and will not be upgraded", [Name]), - prepare_locks(Names, Deps, Locks, Unlocks, Dict); + ?WARN("Dependency ~ts has been removed and will not be upgraded", [Name]), + prepare_locks(Names, Deps, Locks, Unlocks, Dict, AltDeps); Dep -> {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks, Dict), prepare_locks(Names, Deps, NewLocks, - [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict) + [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict, AltDeps) end; {_, _, Level} = Lock when Level > 0 -> case rebar_utils:tup_find(AtomName, Deps) of @@ -137,10 +164,15 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict) -> Dep -> % Dep has been promoted {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks, Dict), prepare_locks(Names, Deps, NewLocks, - [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict) + [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict, AltDeps) end; false -> - ?PRV_ERROR({unknown_dependency, Name}) + case rebar_utils:tup_find(AtomName, AltDeps) of + false -> + ?PRV_ERROR({unknown_dependency, Name}); + _ -> % non-default profile dependency found, pass through + prepare_locks(Names, Deps, Locks, Unlocks, Dict, AltDeps) + end end. prepare_lock(Dep, Lock, Locks, Dict) -> @@ -149,7 +181,7 @@ prepare_lock(Dep, Lock, Locks, Dict) -> {Name, _, Src} -> {Name, Src}; _ when is_atom(Dep) -> %% version-free package. Must unlock whatever matches in locks - {_, Vsn, _} = lists:keyfind(ec_cnv:to_binary(Dep), 1, Locks), + {_, Vsn, _} = lists:keyfind(rebar_utils:to_binary(Dep), 1, Locks), {Dep, Vsn} end, Children = all_children(Name1, Dict), @@ -165,7 +197,7 @@ unlock_children(Children, Locks) -> unlock_children(_, [], Locks, Unlocks) -> {Locks, Unlocks}; unlock_children(Children, [App = {Name,_,_} | Apps], Locks, Unlocks) -> - case lists:member(ec_cnv:to_binary(Name), Children) of + case lists:member(rebar_utils:to_binary(Name), Children) of true -> unlock_children(Children, Apps, Locks, [App | Unlocks]); false -> @@ -183,7 +215,7 @@ all_children(Name, Dict) -> lists:flatten(all_children_(Name, Dict)). all_children_(Name, Dict) -> - case dict:find(ec_cnv:to_binary(Name), Dict) of + case dict:find(rebar_utils:to_binary(Name), Dict) of {ok, Children} -> [Children | [all_children_(Child, Dict) || Child <- Children]]; error -> diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl index c4e72e7..2405ebb 100644 --- a/src/rebar_prv_xref.erl +++ b/src/rebar_prv_xref.erl @@ -36,6 +36,7 @@ init(State) -> -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> + OldPath = code:get_path(), code:add_pathsa(rebar_state:code_paths(State, all_deps)), XrefChecks = prepare(State), XrefIgnores = rebar_state:get(State, xref_ignores, []), @@ -47,7 +48,7 @@ do(State) -> QueryChecks = rebar_state:get(State, xref_queries, []), QueryResults = lists:foldl(fun check_query/2, [], QueryChecks), stopped = xref:stop(xref), - rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)), + rebar_utils:cleanup_code_path(OldPath), case XrefResults =:= [] andalso QueryResults =:= [] of true -> {ok, State}; @@ -70,7 +71,7 @@ short_desc() -> desc() -> io_lib:format( - "~s~n" + "~ts~n" "~n" "Valid rebar.config options:~n" " ~p~n" @@ -97,8 +98,11 @@ prepare(State) -> rebar_state:get(State, xref_warnings, false)}, {verbose, rebar_log:is_verbose(State)}]), - [{ok, _} = xref:add_directory(xref, rebar_app_info:ebin_dir(App)) - || App <- rebar_state:project_apps(State)], + [{ok, _} = xref:add_directory(xref, Dir) + || App <- rebar_state:project_apps(State), + %% the directory may not exist in rare cases of a compile + %% hook of a dep running xref prior to the full job being done + Dir <- [rebar_app_info:ebin_dir(App)], filelib:is_dir(Dir)], %% Get list of xref checks we want to run ConfXrefChecks = rebar_state:get(State, xref_checks, @@ -165,8 +169,15 @@ keyall(Key, List) -> lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List). get_behaviour_callbacks(exports_not_used, Attributes) -> - [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes) ++ - keyall(behavior, Attributes)]; + lists:map(fun(Mod) -> + try + Mod:behaviour_info(callbacks) + catch + error:undef -> + ?WARN("Behaviour ~p is used but cannot be found.", [Mod]), + [] + end + end, keyall(behaviour, Attributes) ++ keyall(behavior, Attributes)); get_behaviour_callbacks(_XrefCheck, _Attributes) -> []. @@ -193,7 +204,7 @@ display_results(XrefResults, QueryResults) -> lists:map(fun display_query_result/1, QueryResults)]. display_query_result({Query, Answer, Value}) -> - io_lib:format("Query ~s~n answer ~p~n did not match ~p~n", + io_lib:format("Query ~ts~n answer ~p~n did not match ~p~n", [Query, Answer, Value]). display_xref_results_for_type({Type, XrefResults}) -> @@ -214,37 +225,37 @@ display_xref_result_fun(Type) -> end, case Type of undefined_function_calls -> - io_lib:format("~sWarning: ~s calls undefined function ~s (Xref)\n", + io_lib:format("~tsWarning: ~ts calls undefined function ~ts (Xref)\n", [Source, SMFA, TMFA]); undefined_functions -> - io_lib:format("~sWarning: ~s is undefined function (Xref)\n", + io_lib:format("~tsWarning: ~ts is undefined function (Xref)\n", [Source, SMFA]); locals_not_used -> - io_lib:format("~sWarning: ~s is unused local function (Xref)\n", + io_lib:format("~tsWarning: ~ts is unused local function (Xref)\n", [Source, SMFA]); exports_not_used -> - io_lib:format("~sWarning: ~s is unused export (Xref)\n", + io_lib:format("~tsWarning: ~ts is unused export (Xref)\n", [Source, SMFA]); deprecated_function_calls -> - io_lib:format("~sWarning: ~s calls deprecated function ~s (Xref)\n", + io_lib:format("~tsWarning: ~ts calls deprecated function ~ts (Xref)\n", [Source, SMFA, TMFA]); deprecated_functions -> - io_lib:format("~sWarning: ~s is deprecated function (Xref)\n", + io_lib:format("~tsWarning: ~ts is deprecated function (Xref)\n", [Source, SMFA]); Other -> - io_lib:format("~sWarning: ~s - ~s xref check: ~s (Xref)\n", + io_lib:format("~tsWarning: ~ts - ~ts xref check: ~ts (Xref)\n", [Source, SMFA, TMFA, Other]) end end. format_mfa({M, F, A}) -> - ?FMT("~s:~s/~w", [M, F, A]). + ?FMT("~ts:~ts/~w", [M, F, A]). format_mfa_source(MFA) -> case find_mfa_source(MFA) of {module_not_found, function_not_found} -> ""; - {Source, function_not_found} -> ?FMT("~s: ", [Source]); - {Source, Line} -> ?FMT("~s:~w: ", [Source, Line]) + {Source, function_not_found} -> ?FMT("~ts: ", [Source]); + {Source, Line} -> ?FMT("~ts:~w: ", [Source, Line]) end. %% @@ -270,12 +281,21 @@ find_mfa_source({M, F, A}) -> end. find_function_source(M, F, A, Bin) -> - AbstractCode = beam_lib:chunks(Bin, [abstract_code]), - {ok, {M, [{abstract_code, {raw_abstract_v1, Code}}]}} = AbstractCode, + ChunksLookup = beam_lib:chunks(Bin, [abstract_code]), + {ok, {M, [{abstract_code, AbstractCodeLookup}]}} = ChunksLookup, + case AbstractCodeLookup of + no_abstract_code -> + % There isn't much else we can do at this point + {module_not_found, function_not_found}; + {raw_abstract_v1, AbstractCode} -> + find_function_source_in_abstract_code(F, A, AbstractCode) + end. + +find_function_source_in_abstract_code(F, A, AbstractCode) -> %% Extract the original source filename from the abstract code - [{attribute, _, file, {Source, _}} | _] = Code, + [{attribute, _, file, {Source, _}} | _] = AbstractCode, %% Extract the line number for a given function def - Fn = [E || E <- Code, + Fn = [E || E <- AbstractCode, safe_element(1, E) == function, safe_element(3, E) == F, safe_element(4, E) == A], diff --git a/src/rebar_relx.erl b/src/rebar_relx.erl index 5c653a3..17c0bd6 100644 --- a/src/rebar_relx.erl +++ b/src/rebar_relx.erl @@ -6,6 +6,10 @@ -export([do/4, format_error/1]). +-ifdef(TEST). +-export([merge_overlays/1]). +-endif. + -include("rebar.hrl"). %% =================================================================== @@ -26,19 +30,21 @@ do(Module, Command, Provider, State) -> AllOptions = string:join([Command | Options], " "), Cwd = rebar_state:dir(State), Providers = rebar_state:providers(State), + RebarOpts = rebar_state:opts(State), + ErlOpts = rebar_opts:erl_opts(RebarOpts), rebar_hooks:run_project_and_app_hooks(Cwd, pre, Provider, Providers, State), try case rebar_state:get(State, relx, []) of [] -> relx:main([{lib_dirs, LibDirs} ,{caller, api} - ,{log_level, LogLevel} | output_dir(OutputDir, Options)], AllOptions); + ,{log_level, LogLevel} | output_dir(OutputDir, Options)] ++ ErlOpts, AllOptions); Config -> Config1 = merge_overlays(Config), relx:main([{lib_dirs, LibDirs} ,{config, Config1} ,{caller, api} - ,{log_level, LogLevel} | output_dir(OutputDir, Options)], AllOptions) + ,{log_level, LogLevel} | output_dir(OutputDir, Options)] ++ ErlOpts, AllOptions) end, rebar_hooks:run_project_and_app_hooks(Cwd, post, Provider, Providers, State), {ok, State} @@ -62,5 +68,5 @@ merge_overlays(Config) -> (_) -> false end, Config), %% Have profile overlay entries come before others to match how profiles work elsewhere - NewOverlay = lists:reverse(lists:flatmap(fun({overlay, Overlay}) -> Overlay end, Overlays)), + NewOverlay = lists:flatmap(fun({overlay, Overlay}) -> Overlay end, lists:reverse(Overlays)), [{overlay, NewOverlay} | Others]. diff --git a/src/rebar_state.erl b/src/rebar_state.erl index 9683709..3314a11 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -182,7 +182,7 @@ all(_, []) -> all(Dir, [File|Artifacts]) -> case filelib:is_regular(filename:join(Dir, File)) of false -> - ?DEBUG("Missing artifact ~s", [filename:join(Dir, File)]), + ?DEBUG("Missing artifact ~ts", [filename:join(Dir, File)]), {false, File}; true -> all(Dir, Artifacts) @@ -302,9 +302,9 @@ dir(State=#state_t{}, Dir) -> deps_names(Deps) when is_list(Deps) -> lists:map(fun(Dep) when is_tuple(Dep) -> - ec_cnv:to_binary(element(1, Dep)); + rebar_utils:to_binary(element(1, Dep)); (Dep) when is_atom(Dep) -> - ec_cnv:to_binary(Dep) + rebar_utils:to_binary(Dep) end, Deps); deps_names(State) -> Deps = rebar_state:get(State, deps, []), diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 9b33ec5..75190ec 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -129,7 +129,7 @@ default_author_and_email() -> %% Ok, try mecurial case rebar_utils:sh("hg showconfig ui.username", [return_on_error]) of {ok, NameEmail} -> - case re:run(NameEmail, "^(.*) <(.*)>$", [{capture, [1,2], list}]) of + case re:run(NameEmail, "^(.*) <(.*)>$", [{capture, [1,2], list}, unicode]) of {match, [Name, Email]} -> {Name, Email}; _ -> @@ -169,7 +169,7 @@ maybe_warn_about_name(Vars) -> invalid -> ?WARN("The 'name' variable is often associated with Erlang " "module names and/or file names. The value submitted " - "(~s) isn't an unquoted Erlang atom. Templates " + "(~ts) isn't an unquoted Erlang atom. Templates " "generated may contain errors.", [Name]); valid -> @@ -189,7 +189,7 @@ validate_atom(Str) -> %% Run template instructions one at a time. execute_template([], _, {Template,_,_}, _, _) -> - ?DEBUG("Template ~s applied", [Template]), + ?DEBUG("Template ~ts applied", [Template]), ok; %% We can't execute the description execute_template([{description, _} | Terms], Files, Template, Vars, Force) -> @@ -242,7 +242,7 @@ execute_template([{template, From, To} | Terms], Files, {Template, Type, Cwd}, V execute_template(Terms, Files, {Template, Type, Cwd}, Vars, Force); %% Unknown execute_template([Instruction|Terms], Files, Tpl={Template,_,_}, Vars, Force) -> - ?WARN("Unknown template instruction ~p in template ~s", + ?WARN("Unknown template instruction ~p in template ~ts", [Instruction, Template]), execute_template(Terms, Files, Tpl, Vars, Force). @@ -305,7 +305,7 @@ cache_escript_files(State) -> find_escript_templates(Files) -> [{escript, Name} || {Name, _Bin} <- Files, - re:run(Name, ?TEMPLATE_RE, [{capture, none}]) == match]. + re:run(Name, ?TEMPLATE_RE, [{capture, none}, unicode]) == match]. find_localinstall_templates(_State) -> Templates = rebar_utils:find_files(code:priv_dir(rebar), ?TEMPLATE_RE), @@ -334,8 +334,19 @@ find_plugin_templates(State) -> || App <- rebar_state:all_plugin_deps(State), Priv <- [rebar_app_info:priv_dir(App)], Priv =/= undefined, + File <- rebar_utils:find_files(Priv, ?TEMPLATE_RE)] + ++ %% and add global plugins too + [{plugin, File} + || PSource <- rebar_state:get(State, {plugins, global}, []), + Plugin <- [plugin_provider(PSource)], + is_atom(Plugin), + Priv <- [code:priv_dir(Plugin)], + Priv =/= undefined, File <- rebar_utils:find_files(Priv, ?TEMPLATE_RE)]. +plugin_provider(P) when is_atom(P) -> P; +plugin_provider(T) when is_tuple(T) -> element(1, T). + %% Take an existing list of templates and tag them by name the way %% the user would enter it from the CLI tag_names(List) -> @@ -363,7 +374,7 @@ prioritize_templates([{Name, Type, File} | Rest], Valid) -> prioritize_templates(Rest, Valid); {_, file, _} -> ?DEBUG("Skipping template ~p, due to presence of a custom " - "template at ~s", [Name, File]), + "template at ~ts", [Name, File]), prioritize_templates(Rest, Valid) end. @@ -418,10 +429,10 @@ write_file(Output, Data, Force) -> ok = filelib:ensure_dir(Output), case {Force, FileExists} of {true, true} -> - ?INFO("Writing ~s (forcibly overwriting)", + ?INFO("Writing ~ts (forcibly overwriting)", [Output]); _ -> - ?INFO("Writing ~s", [Output]) + ?INFO("Writing ~ts", [Output]) end, case file:write_file(Output, Data) of ok -> @@ -438,4 +449,4 @@ write_file(Output, Data, Force) -> %% Render a binary to a string, using mustache and the specified context %% render(Bin, Context) -> - bbmustache:render(ec_cnv:to_binary(Bin), Context, [{key_type, atom}]). + bbmustache:render(rebar_utils:to_binary(Bin), Context, [{key_type, atom}]). diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index c357e94..016847e 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -55,6 +55,8 @@ get_arch/0, wordsize/0, deps_to_binary/1, + to_binary/1, + to_list/1, tup_dedup/1, tup_umerge/2, tup_sort/1, @@ -115,7 +117,7 @@ filtermap(F, [Hd|Tail]) -> filtermap(F, []) when is_function(F, 1) -> []. is_arch(ArchRegex) -> - case re:run(get_arch(), ArchRegex, [{capture, none}]) of + case re:run(get_arch(), ArchRegex, [{capture, none}, unicode]) of match -> true; nomatch -> @@ -142,7 +144,7 @@ wordsize() -> end. sh_send(Command0, String, Options0) -> - ?INFO("sh_send info:\n\tcwd: ~p\n\tcmd: ~s < ~s\n", + ?INFO("sh_send info:\n\tcwd: ~p\n\tcmd: ~ts < ~ts\n", [rebar_dir:get_cwd(), Command0, String]), ?DEBUG("\topts: ~p\n", [Options0]), @@ -171,7 +173,7 @@ sh_send(Command0, String, Options0) -> %% Val = string() | false %% sh(Command0, Options0) -> - ?DEBUG("sh info:\n\tcwd: ~p\n\tcmd: ~s\n", [rebar_dir:get_cwd(), Command0]), + ?DEBUG("sh info:\n\tcwd: ~p\n\tcmd: ~ts\n", [rebar_dir:get_cwd(), Command0]), ?DEBUG("\topts: ~p\n", [Options0]), DefaultOptions = [{use_stdout, false}, debug_and_abort_on_error], @@ -184,7 +186,7 @@ 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, eof], - ?DEBUG("Port Cmd: ~s\nPort Opts: ~p\n", [Command, PortSettings]), + ?DEBUG("Port Cmd: ~ts\nPort Opts: ~p\n", [Command, PortSettings]), Port = open_port({spawn, Command}, PortSettings), try @@ -232,7 +234,7 @@ deprecated(Old, New, When) -> <<"WARNING: deprecated ~p option used~n" "Option '~p' has been deprecated~n" "in favor of '~p'.~n" - "'~p' will be removed ~s.~n">>, + "'~p' will be removed ~ts.~n">>, [Old, Old, New, Old, When]). %% for use by `do` task @@ -244,11 +246,17 @@ args_to_tasks(Args) -> new_task(Args, []). deps_to_binary([]) -> []; deps_to_binary([{Name, _, Source} | T]) -> - [{ec_cnv:to_binary(Name), Source} | deps_to_binary(T)]; + [{to_binary(Name), Source} | deps_to_binary(T)]; deps_to_binary([{Name, Source} | T]) -> - [{ec_cnv:to_binary(Name), Source} | deps_to_binary(T)]; + [{to_binary(Name), Source} | deps_to_binary(T)]; deps_to_binary([Name | T]) -> - [ec_cnv:to_binary(Name) | deps_to_binary(T)]. + [to_binary(Name) | deps_to_binary(T)]. + +to_binary(A) when is_atom(A) -> atom_to_binary(A, unicode); +to_binary(Str) -> unicode:characters_to_binary(Str). + +to_list(A) when is_atom(A) -> atom_to_list(A); +to_list(Str) -> unicode:characters_to_list(Str). tup_dedup(List) -> tup_dedup_(tup_sort(List)). @@ -396,10 +404,10 @@ check_min_otp_version(MinOtpVersion) -> case ParsedVsn >= ParsedMin of true -> - ?DEBUG("~s satisfies the requirement for minimum OTP version ~s", + ?DEBUG("~ts satisfies the requirement for minimum OTP version ~ts", [OtpRelease, MinOtpVersion]); false -> - ?ABORT("OTP release ~s or later is required. Version in use: ~s", + ?ABORT("OTP release ~ts or later is required. Version in use: ~ts", [MinOtpVersion, OtpRelease]) end. @@ -415,16 +423,16 @@ check_blacklisted_otp_versions(BlacklistedRegexes) -> abort_if_blacklisted(BlacklistedRegex, OtpRelease) -> case re:run(OtpRelease, BlacklistedRegex, [{capture, none}]) of match -> - ?ABORT("OTP release ~s matches blacklisted version ~s", + ?ABORT("OTP release ~ts matches blacklisted version ~ts", [OtpRelease, BlacklistedRegex]); nomatch -> - ?DEBUG("~s does not match blacklisted OTP version ~s", + ?DEBUG("~ts does not match blacklisted OTP version ~ts", [OtpRelease, BlacklistedRegex]) end. user_agent() -> {ok, Vsn} = application:get_key(rebar, vsn), - ?FMT("Rebar/~s (OTP/~s)", [Vsn, otp_release()]). + ?FMT("Rebar/~ts (OTP/~ts)", [Vsn, otp_release()]). reread_config(ConfigList) -> %% NB: we attempt to mimic -config here, which survives app reload, @@ -457,7 +465,7 @@ version_tuple(OtpRelease) -> {match, [_Full, Maj]} -> {list_to_integer(Maj), 0, 0}; nomatch -> - ?ABORT("Minimum OTP release unable to be parsed: ~s", [OtpRelease]) + ?ABORT("Minimum OTP release unable to be parsed: ~ts", [OtpRelease]) end. otp_release() -> @@ -510,7 +518,7 @@ patch_on_windows(Cmd, Env) -> end, Cmd, Env), %% Remove left-over vars re:replace(Cmd1, "\\\$\\w+|\\\${\\w+}", "", - [global, {return, list}]); + [global, {return, list}, unicode]); _ -> Cmd end. @@ -529,7 +537,7 @@ expand_env_variable(InStr, VarName, RawVarValue) -> VarValue = re:replace(RawVarValue, "\\\\", "\\\\\\\\", ReOpts), %% Use a regex to match/replace: %% Given variable "FOO": match $FOO\s | $FOOeol | ${FOO} - RegEx = io_lib:format("\\\$(~s(\\W|$)|{~s})", [VarName, VarName]), + RegEx = io_lib:format("\\\$(~ts(\\W|$)|{~ts})", [VarName, VarName]), re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts) end. @@ -554,7 +562,7 @@ expand_sh_flag(use_stdout) -> {output_handler, fun(Line, Acc) -> %% Line already has a newline so don't use ?CONSOLE which adds one - io:format("~s", [Line]), + io:format("~ts", [Line]), [Line | Acc] end}; expand_sh_flag({use_stdout, false}) -> @@ -577,23 +585,23 @@ log_msg_and_abort(Message) -> -spec debug_log_msg_and_abort(string()) -> err_handler(). debug_log_msg_and_abort(Message) -> fun(Command, {Rc, Output}) -> - ?DEBUG("sh(~s)~n" + ?DEBUG("sh(~ts)~n" "failed with return code ~w and the following output:~n" - "~s", [Command, Rc, Output]), + "~ts", [Command, Rc, Output]), ?ABORT(Message, []) end. -spec log_and_abort(string(), {integer(), string()}) -> no_return(). log_and_abort(Command, {Rc, Output}) -> - ?ABORT("sh(~s)~n" + ?ABORT("sh(~ts)~n" "failed with return code ~w and the following output:~n" - "~s", [Command, Rc, Output]). + "~ts", [Command, Rc, Output]). -spec debug_and_abort(string(), {integer(), string()}) -> no_return(). debug_and_abort(Command, {Rc, Output}) -> - ?DEBUG("sh(~s)~n" + ?DEBUG("sh(~ts)~n" "failed with return code ~w and the following output:~n" - "~s", [Command, Rc, Output]), + "~ts", [Command, Rc, Output]), throw(rebar_abort). sh_loop(Port, Fun, Acc) -> @@ -662,7 +670,7 @@ vcs_vsn(Vcs, Dir, Resources) -> unknown -> ?ABORT("vcs_vsn: Unknown vsn format: ~p", [Vcs]); {error, Reason} -> - ?ABORT("vcs_vsn: ~s", [Reason]) + ?ABORT("vcs_vsn: ~ts", [Reason]) end. %% Temp work around for repos like relx that use "semver" @@ -779,7 +787,7 @@ cleanup_code_path(OrigPath) -> new_task([], Acc) -> lists:reverse(Acc); new_task([TaskList|Rest], Acc) -> - case re:split(TaskList, ",", [{return, list}, {parts, 2}]) of + case re:split(TaskList, ",", [{return, list}, {parts, 2}, unicode]) of %% `do` consumes all remaining args ["do" = Task] -> lists:reverse([{Task, Rest}|Acc]); @@ -806,7 +814,7 @@ arg_or_flag(["-" ++ _ = Flag|Rest], [{Task, Args}|Acc]) -> end; %% an argument or a sequence of arguments arg_or_flag([ArgList|Rest], [{Task, Args}|Acc]) -> - case re:split(ArgList, ",", [{return, list}, {parts, 2}]) of + case re:split(ArgList, ",", [{return, list}, {parts, 2}, unicode]) of %% single arg terminated by a comma [Arg, ""] -> new_task(Rest, [{Task, lists:reverse([Arg|Args])}|Acc]); @@ -857,15 +865,18 @@ url_append_path(Url, ExtraPath) -> escape_chars(Str) when is_atom(Str) -> escape_chars(atom_to_list(Str)); escape_chars(Str) -> - re:replace(Str, "([ ()?`!$&;])", "\\\\&", [global, {return, list}]). + re:replace(Str, "([ ()?`!$&;\"\'])", "\\\\&", + [global, {return, list}, unicode]). %% "escape inside these" escape_double_quotes(Str) -> - re:replace(Str, "([\"\\\\`!$&*;])", "\\\\&", [global, {return, list}]). + re:replace(Str, "([\"\\\\`!$&*;])", "\\\\&", + [global, {return, list}, unicode]). %% "escape inside these" but allow * escape_double_quotes_weak(Str) -> - re:replace(Str, "([\"\\\\`!$&;])", "\\\\&", [global, {return, list}]). + re:replace(Str, "([\"\\\\`!$&;])", "\\\\&", + [global, {return, list}, unicode]). info_useless(Old, New) -> [?INFO("App ~ts is no longer needed and can be deleted.", [Name]) diff --git a/test/rebar_as_SUITE.erl b/test/rebar_as_SUITE.erl index ce8046b..78ea8ae 100644 --- a/test/rebar_as_SUITE.erl +++ b/test/rebar_as_SUITE.erl @@ -176,7 +176,7 @@ error_on_empty_tasks(Config) -> warn_match(App, History) -> lists:any( - fun({_, {rebar_log,log, [warn, "No entry for profile ~s in config.", + fun({_, {rebar_log,log, [warn, "No entry for profile ~ts in config.", [ArgApp]]}, _}) -> ArgApp =:= App ; (_) -> false diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 9f01496..5a18745 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -78,21 +78,10 @@ all() -> recompile_when_parse_transform_as_opt_changes, recompile_when_parse_transform_inline_changes, regex_filter_skip, regex_filter_regression, - %% recompile behaviour when `ERL_COMPILER_OPTIONS` differs prior to 19.x - recursive, no_recursive] ++ recompile_when_env_changes_test(). - -recompile_when_env_changes_test() -> - _ = code:ensure_loaded(os), - UnSetEnv = erlang:function_exported(os, unsetenv, 1), - _ = code:ensure_loaded(compile), - EnvOpts = erlang:function_exported(compile, env_compiler_options, 0), - case {UnSetEnv, EnvOpts} of - {true, true} -> - [dont_recompile_when_erl_compiler_options_env_does_not_change, - recompile_when_erl_compiler_options_env_changes]; - {true, false} -> [always_recompile_when_erl_compiler_options_set]; - {false, _} -> [] - end. + recursive, no_recursive, + always_recompile_when_erl_compiler_options_set, + dont_recompile_when_erl_compiler_options_env_does_not_change, + recompile_when_erl_compiler_options_env_changes]. groups() -> [{basic_app, [], [build_basic_app, paths_basic_app, clean_basic_app]}, @@ -266,7 +255,31 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. -init_per_testcase(_, Config) -> +init_per_testcase(Test, Config) when + Test == dont_recompile_when_erl_compiler_options_env_does_not_change + orelse + Test == recompile_when_erl_compiler_options_env_changes -> + _ = code:ensure_loaded(os), + UnSetEnv = erlang:function_exported(os, unsetenv, 1), + _ = code:ensure_loaded(compile), + EnvOpts = erlang:function_exported(compile, env_compiler_options, 0), + case {UnSetEnv, EnvOpts} of + {true, true} -> maybe_init_config(Config); + _ -> {skip, "compile:env_compiler_options/0 unavailable"} + end; +init_per_testcase(always_recompile_when_erl_compiler_options_set, Config) -> + _ = code:ensure_loaded(os), + UnSetEnv = erlang:function_exported(os, unsetenv, 1), + _ = code:ensure_loaded(compile), + EnvOpts = erlang:function_exported(compile, env_compiler_options, 0), + case {UnSetEnv, EnvOpts} of + {true, true} -> {skip, "compile:env_compiler_options/0 available"}; + {true, false} -> maybe_init_config(Config); + _ -> {skip, "os:unsetenv/1 unavailable"} + end; +init_per_testcase(_, Config) -> maybe_init_config(Config). + +maybe_init_config(Config) -> case ?config(apps, Config) of undefined -> rebar_test_utils:init_rebar_state(Config); _ -> Config @@ -276,7 +289,6 @@ end_per_testcase(_, _Config) -> catch meck:unload(). - %% test cases build_basic_app(Config) -> diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 06dc76e..70cf60c 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -55,7 +55,9 @@ testspec/1, testspec_at_root/1, testspec_parse_error/1, - cmd_vs_cfg_opts/1]). + cmd_vs_cfg_opts/1, + single_testspec_in_ct_opts/1, + compile_only/1]). -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -75,7 +77,9 @@ all() -> [{group, basic_app}, testspec, testspec_at_root, testspec_parse_error, - cmd_vs_cfg_opts]. + cmd_vs_cfg_opts, + single_testspec_in_ct_opts, + compile_only]. groups() -> [{basic_app, [], [basic_app_default_dirs, basic_app_default_beams, @@ -1548,6 +1552,57 @@ cmd_vs_cfg_opts(Config) -> ok. +single_testspec_in_ct_opts(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "ct_testspec_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name("ct_testspec_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + Spec = filename:join([AppDir, "test", "some.spec"]), + ok = filelib:ensure_dir(Spec), + ok = file:write_file(Spec, "{suites,\".\",all}.\n"), + + {ok,Wd} = file:get_cwd(), + ok = file:set_cwd(AppDir), + + RebarConfig = [{ct_opts, [{spec,"test/some.spec"}]}], + + {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return), + + Providers = rebar_state:providers(State), + Namespace = rebar_state:namespace(State), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + + %% Testspec in "test" directory + {ok, GetOptResult1} = getopt:parse(GetOptSpec, []), + State1 = rebar_state:command_parsed_args(State, GetOptResult1), + Tests1 = rebar_prv_common_test:prepare_tests(State1), + {ok, T1} = Tests1, + "test/some.spec" = proplists:get_value(spec,T1), + {ok, _NewState} = rebar_prv_common_test:compile(State1, Tests1), + + ok = file:set_cwd(Wd), + ok. + +compile_only(Config) -> + C = rebar_test_utils:init_rebar_state(Config, "compile_only_"), + + AppDir = ?config(apps, C), + + Name = rebar_test_utils:create_random_name(atom_to_list(basic_app) ++ "_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + Suite = filename:join([AppDir, "test", Name ++ "_SUITE.erl"]), + ok = filelib:ensure_dir(Suite), + ok = file:write_file(Suite, test_suite(Name)), + + {ok, _State} = rebar_test_utils:run_and_check(C, [], ["ct", "--compile_only"], {ok, [{app,Name}], "test"}). + + %% helper for generating test data test_suite(Name) -> io_lib:format("-module(~ts_SUITE).\n" @@ -1556,7 +1611,7 @@ test_suite(Name) -> "some_test(_) -> ok.\n", [Name]). cmd_sys_config_file(AppName) -> - io_lib:format("[{~s, [{key, cmd_value}]}].", [AppName]). + io_lib:format("[{~ts, [{key, cmd_value}]}].", [AppName]). cfg_sys_config_file(AppName) -> - io_lib:format("[{~s, [{key, cfg_value}]}].", [AppName]). + io_lib:format("[{~ts, [{key, cfg_value}]}].", [AppName]). diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl index 6b2ecea..ae50ea3 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -7,7 +7,7 @@ all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, http_os_proxy_settings, https_os_proxy_settings, semver_matching_lt, semver_matching_lte, semver_matching_gt, - valid_version, {group, git}, {group, pkg}]. + valid_version, top_override, {group, git}, {group, pkg}]. groups() -> [{all, [], [flat, pick_highest_left, pick_highest_right, @@ -47,6 +47,8 @@ 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(top_override, Config) -> + rebar_test_utils:init_rebar_state(Config); init_per_testcase(http_proxy_settings, Config) -> %% Create private rebar.config Priv = ?config(priv_dir, Config), @@ -255,6 +257,32 @@ circular1(Config) -> run(Config). circular2(Config) -> run(Config). circular_skip(Config) -> run(Config). +%% Test that a top-level application overtakes dependencies, and +%% works even if said deps do not exist. +top_override(Config) -> + AppDir = ?config(apps, Config), + ct:pal("dir: ~p", [AppDir]), + Name1 = rebar_test_utils:create_random_name("sub_app1_"), + Name2 = rebar_test_utils:create_random_name("sub_app2_"), + SubAppsDir1 = filename:join([AppDir, "apps", Name1]), + SubAppsDir2 = filename:join([AppDir, "apps", Name2]), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(SubAppsDir1, Name1, Vsn, [kernel, stdlib]), + rebar_test_utils:create_app(SubAppsDir2, Name2, Vsn, [kernel, stdlib]), + rebar_test_utils:create_config( + SubAppsDir1, + [{deps, [list_to_atom(Name2)]}] + ), + rebar_test_utils:create_config( + SubAppsDir2, + [{deps, [{list_to_atom(Name1), + {git, "https://example.org", {branch, "master"}}}]}] + ), + rebar_test_utils:run_and_check( + Config, [], ["compile"], + {ok, [{app, Name1}, {app,Name2}]} + ). + %% Test that the deps of project apps that have their own rebar.config %% are included, but that top level rebar.config deps take precedence sub_app_deps(Config) -> diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl index 6fb325b..4805b3d 100644 --- a/test/rebar_eunit_SUITE.erl +++ b/test/rebar_eunit_SUITE.erl @@ -1,7 +1,8 @@ -module(rebar_eunit_SUITE). -export([all/0, groups/0]). --export([init_per_suite/1, init_per_group/2, end_per_group/2]). +-export([init_per_suite/1, end_per_suite/1]). +-export([init_per_group/2, end_per_group/2]). -export([basic_app_compiles/1, basic_app_files/1]). -export([basic_app_exports/1, basic_app_testset/1]). -export([basic_app_eunit_macro/1]). @@ -60,6 +61,8 @@ init_per_suite(Config) -> {ok, _} = zip:extract(filename:join([PrivDir, "multi_app.zip"]), [{cwd, PrivDir}]), Config. +end_per_suite(Config) -> Config. + init_per_group(basic_app, Config) -> GroupState = rebar_test_utils:init_rebar_state(Config, "basic_app_"), diff --git a/test/rebar_hooks_SUITE.erl b/test/rebar_hooks_SUITE.erl index b121dd5..f7fcb82 100644 --- a/test/rebar_hooks_SUITE.erl +++ b/test/rebar_hooks_SUITE.erl @@ -9,6 +9,7 @@ build_and_clean_app/1, escriptize_artifacts/1, run_hooks_once/1, + run_hooks_once_profiles/1, run_hooks_for_plugins/1, eunit_app_hooks/1, deps_hook_namespace/1]). @@ -33,8 +34,9 @@ end_per_testcase(_, _Config) -> catch meck:unload(). all() -> - [build_and_clean_app, run_hooks_once, escriptize_artifacts, - run_hooks_for_plugins, deps_hook_namespace, eunit_app_hooks]. + [build_and_clean_app, run_hooks_once, run_hooks_once_profiles, + escriptize_artifacts, run_hooks_for_plugins, deps_hook_namespace, + eunit_app_hooks]. %% Test post provider hook cleans compiled project app, leaving it invalid build_and_clean_app(Config) -> @@ -98,6 +100,18 @@ run_hooks_once(Config) -> rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name, valid}]}). +%% test that even if a hook is defined at the project level in a used profile +%% the hook is not run for each application in the project umbrella +run_hooks_once_profiles(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + RebarConfig = [{profiles, [{hooks, [{pre_hooks, [{compile, "mkdir blah"}]}]}]}], + rebar_test_utils:create_config(AppDir, RebarConfig), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + rebar_test_utils:run_and_check(Config, RebarConfig, ["as", "hooks", "compile"], {ok, [{app, Name, valid}]}). + deps_hook_namespace(Config) -> mock_git_resource:mock([{deps, [{some_dep, "0.0.1"}]}]), Deps = rebar_test_utils:expand_deps(git, [{"some_dep", "0.0.1", []}]), diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl index 30cc0a8..32873c8 100644 --- a/test/rebar_pkg_SUITE.erl +++ b/test/rebar_pkg_SUITE.erl @@ -287,7 +287,7 @@ mock_config(Name, Config) -> unmock_config(Config) -> meck:unload(), - ets:delete(?config(mock_table, Config)). + catch ets:delete(?config(mock_table, Config)). copy_to_cache({Pkg,Vsn}, Config) -> Name = <<Pkg/binary, "-", Vsn/binary, ".tar">>, diff --git a/test/rebar_profiles_SUITE.erl b/test/rebar_profiles_SUITE.erl index ed492a9..9ffaf98 100644 --- a/test/rebar_profiles_SUITE.erl +++ b/test/rebar_profiles_SUITE.erl @@ -25,7 +25,8 @@ test_profile_erl_opts_order_2/1, test_profile_erl_opts_order_3/1, test_profile_erl_opts_order_4/1, - test_profile_erl_opts_order_5/1]). + test_profile_erl_opts_order_5/1, + first_files_exception/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -46,7 +47,8 @@ all() -> test_profile_erl_opts_order_2, test_profile_erl_opts_order_3, test_profile_erl_opts_order_4, - test_profile_erl_opts_order_5]. + test_profile_erl_opts_order_5, + first_files_exception]. init_per_suite(Config) -> application:start(meck), @@ -468,6 +470,25 @@ test_profile_erl_opts_order_5(Config) -> Opt = last_erl_opt(Opts, [warn_export_all, nowarn_export_all], undefined), warn_export_all = Opt. +first_files_exception(_Config) -> + RebarConfig = [{erl_first_files, ["c","a","b"]}, + {mib_first_files, ["c","a","b"]}, + {other, ["c","a","b"]}, + {profiles, + [{profile2, [{erl_first_files, ["a","e"]}, + {mib_first_files, ["a","e"]}, + {other, ["a","e"]} + ]}]}], + State = rebar_state:new(RebarConfig), + State1 = rebar_state:apply_profiles(State, [profile2]), + + %% Combine lists + ?assertEqual(["a","b","c","e"], rebar_state:get(State1, other)), + %% there is no specific reason not to dedupe "a" here aside from "this is how it is" + ?assertEqual(["c","a","b","a","e"], rebar_state:get(State1, erl_first_files)), + ?assertEqual(["c","a","b","a","e"], rebar_state:get(State1, mib_first_files)), + ok. + get_compiled_profile_erl_opts(Profiles, Config) -> AppDir = ?config(apps, Config), PStrs = [atom_to_list(P) || P <- Profiles], diff --git a/test/rebar_release_SUITE.erl b/test/rebar_release_SUITE.erl index 1125a7e..e41339b 100644 --- a/test/rebar_release_SUITE.erl +++ b/test/rebar_release_SUITE.erl @@ -11,6 +11,7 @@ all() -> [release, profile_ordering_sys_config_extend_3_tuple_merge, extend_release, user_output_dir, profile_overlays, + profile_overlay_merge, overlay_vars]. init_per_testcase(Case, Config0) -> @@ -217,6 +218,30 @@ profile_overlays(Config) -> {dir, filename:join(ReleaseDir, "randomdir")}]} ). +profile_overlay_merge (_Config) -> + % when profile and relx overlays both exist, the profile overlays should be + % first, then the relx overlays, all the rest of the config should come + % after, rebar_relx:merge_overlays/1 should do this. + RelxOverlay = [{mkdir, "1_from_relx"}, {mkdir, "2_from_relx"}], + ProfileOverlay = [{mkdir, "0_from_other_profile"}], + OtherConfig = [{other1, config}, {other2, config}], + + % test with no overlays + ?assertEqual([{overlay,[]}] ++ OtherConfig, + rebar_relx:merge_overlays(OtherConfig)), + + % test with relx only, just move overlays to the top + RelxOnly = OtherConfig ++ [{overlay, RelxOverlay}], + ?assertEqual([{overlay, RelxOverlay}]++OtherConfig, + rebar_relx:merge_overlays(RelxOnly)), + + % now test with a profile (profiles end up after relx overlays + ProfilesToMerge = OtherConfig ++ + [{overlay, RelxOverlay}, + {overlay, ProfileOverlay}], + ?assertEqual([{overlay, ProfileOverlay ++ RelxOverlay}] ++ OtherConfig, + rebar_relx:merge_overlays(ProfilesToMerge)). + overlay_vars(Config) -> AppDir = ?config(apps, Config), Name = ?config(name, Config), diff --git a/test/rebar_src_dirs_SUITE.erl b/test/rebar_src_dirs_SUITE.erl index f854a94..bc22160 100644 --- a/test/rebar_src_dirs_SUITE.erl +++ b/test/rebar_src_dirs_SUITE.erl @@ -11,12 +11,16 @@ src_dirs_in_erl_opts/1, extra_src_dirs_in_erl_opts/1, src_dirs_at_root_and_in_erl_opts/1, + dupe_src_dirs_at_root_and_in_erl_opts/1, extra_src_dirs_at_root_and_in_erl_opts/1, build_basic_app/1, build_multi_apps/1, - src_dir_takes_precedence_over_extra/1]). + src_dir_takes_precedence_over_extra/1, + src_dir_checkout_dep/1, + app_src_info/1]). -include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). suite() -> []. @@ -35,8 +39,11 @@ end_per_testcase(_, _Config) -> ok. all() -> [src_dirs_at_root, extra_src_dirs_at_root, src_dirs_in_erl_opts, extra_src_dirs_in_erl_opts, - src_dirs_at_root_and_in_erl_opts, extra_src_dirs_at_root_and_in_erl_opts, - build_basic_app, build_multi_apps, src_dir_takes_precedence_over_extra]. + src_dirs_at_root_and_in_erl_opts, + dupe_src_dirs_at_root_and_in_erl_opts, + extra_src_dirs_at_root_and_in_erl_opts, + build_basic_app, build_multi_apps, src_dir_takes_precedence_over_extra, + src_dir_checkout_dep, app_src_info]. src_dirs_at_root(Config) -> AppDir = ?config(apps, Config), @@ -93,15 +100,47 @@ extra_src_dirs_in_erl_opts(Config) -> src_dirs_at_root_and_in_erl_opts(Config) -> AppDir = ?config(apps, Config), - Name = rebar_test_utils:create_random_name("app1_"), + Name = rebar_test_utils:create_random_name("src_dirs_root_erlopts_"), Vsn = rebar_test_utils:create_random_vsn(), rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}]}, {src_dirs, ["baz", "qux"]}], + %% move the .app.src file to one of the subdirs, out of src/ + filelib:ensure_dir(filename:join([AppDir, "qux", "fake"])), + rebar_file_utils:mv(filename:join([AppDir, "src", Name ++ ".app.src"]), + filename:join([AppDir, "qux", Name ++ ".app.src"])), + {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), - ["bar", "baz", "foo", "qux"] = rebar_dir:src_dirs(rebar_state:opts(State), []). + ["bar", "baz", "foo", "qux"] = rebar_dir:src_dirs(rebar_state:opts(State), []), + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], + {ok, [{app, Name}]}), + ok. + +dupe_src_dirs_at_root_and_in_erl_opts(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("dupe_src_dirs_root_erlopts_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}]}, {src_dirs, ["baz", "qux"]}], + + %% move the .app.src file to one of the subdirs, out of src/ + filelib:ensure_dir(filename:join([AppDir, "qux", "fake"])), + filelib:ensure_dir(filename:join([AppDir, "foo", "fake"])), + Src1 = filename:join([AppDir, "qux", Name ++ ".app.src"]), + Src2 = filename:join([AppDir, "foo", Name ++ ".app.src"]), + rebar_file_utils:mv(filename:join([AppDir, "src", Name ++ ".app.src"]), + Src1), + %% Then copy it over to create a conflict with dupes + file:copy(Src1, Src2), + + {error, {rebar_prv_app_discovery, {multiple_app_files, [Src2, Src1]}}} = + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return), + + ok. extra_src_dirs_at_root_and_in_erl_opts(Config) -> AppDir = ?config(apps, Config), @@ -236,3 +275,52 @@ src_dir_takes_precedence_over_extra(Config) -> [{application, _, KVs}] = App, Mods = proplists:get_value(modules, KVs), true = lists:member(extra, Mods). + +src_dir_checkout_dep(Config) -> + AppDir = ?config(apps, Config), + AppName = rebar_test_utils:create_random_name("src_dir_checkout_app"), + DepName = rebar_test_utils:create_random_name("src_dir_checkout_dep"), + AtomDep = list_to_atom(DepName), + + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, AppName, Vsn, [kernel, stdlib]), + RebarConfig = [{deps, [AtomDep]}], + + DepDir = filename:join([?config(checkouts, Config), DepName]), + ct:pal("checkouts dir: ~p", [DepDir]), + rebar_test_utils:create_app(DepDir, DepName, Vsn, [kernel, stdlib]), + + + %% move the .app.src file to one of the subdirs, out of src/ + rebar_file_utils:mv(filename:join([DepDir, "src"]), + filename:join([DepDir, "qux"])), + DepRebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}]}, + {src_dirs, ["baz", "qux"]}], + file:write_file(filename:join([DepDir, "rebar.config"]), + io_lib:format("~p.~n~p.~n", DepRebarConfig)), + + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{checkout, DepName}, {app, AppName}]} + ), + ok. + +app_src_info(Config) -> + PrivDir = ?config(priv_dir, Config), + AppName1 = rebar_test_utils:create_random_name("app_src_info"), + AppDir1 = filename:join(PrivDir, AppName1), + {ok, Info1} = rebar_app_info:new(AppName1, "1.0.0", AppDir1), + AppSrc1 = filename:join([AppDir1, "src", AppName1 ++ ".app.src"]), + ok = filelib:ensure_dir(AppSrc1), + ok = file:write_file(AppSrc1, "[]."), + ?assertEqual(AppSrc1, rebar_app_info:app_file_src(Info1)), + + AppName2 = rebar_test_utils:create_random_name("app_src_info"), + AppDir2 = filename:join(PrivDir, AppName2), + {ok, Info2Tmp} = rebar_app_info:new(AppName2, "1.0.0", AppDir2), + Info2 = rebar_app_info:set(Info2Tmp, src_dirs, ["foo", "bar", "baz"]), + AppSrc2 = filename:join([AppDir2, "bar", AppName2 ++ ".app.src"]), + ok = filelib:ensure_dir(AppSrc2), + ok = file:write_file(AppSrc2, "[]."), + ?assertEqual(AppSrc2, rebar_app_info:app_file_src(Info2)), + ok. diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 8c177c9..0ccec56 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -218,7 +218,7 @@ 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", "*"]), + CheckoutsDirs = filelib:wildcard(filename:join([AppDir, "_checkouts", "*"])), LockFile = filename:join([AppDir, "rebar.lock"]), Locks = lists:flatten(rebar_config:consult_lock_file(LockFile)), @@ -230,7 +230,7 @@ check_results(AppDir, Expected, ProfileRun) -> Deps = rebar_app_discover:find_apps(BuildDirs, all), DepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Deps], - Checkouts = rebar_app_discover:find_apps([CheckoutsDir], all), + Checkouts = rebar_app_discover:find_apps(CheckoutsDirs, all), CheckoutsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Checkouts], Plugins = rebar_app_discover:find_apps(PluginDirs, all), PluginsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Plugins], @@ -349,7 +349,7 @@ check_results(AppDir, Expected, ProfileRun) -> iolist_to_binary(LockVsn)) end ; ({release, Name, Vsn, ExpectedDevMode}) -> - ct:pal("Release: ~p-~s", [Name, Vsn]), + ct:pal("Release: ~p-~ts", [Name, Vsn]), {ok, Cwd} = file:get_cwd(), try file:set_cwd(AppDir), @@ -377,14 +377,14 @@ check_results(AppDir, Expected, ProfileRun) -> file:set_cwd(Cwd) end ; ({tar, Name, Vsn}) -> - ct:pal("Tarball: ~s-~s", [Name, Vsn]), + ct:pal("Tarball: ~ts-~ts", [Name, Vsn]), Tarball = filename:join([AppDir, "_build", "rel", Name, Name++"-"++Vsn++".tar.gz"]), ?assertNotEqual([], filelib:is_file(Tarball)) ; ({file, Filename}) -> - ct:pal("Filename: ~s", [Filename]), + ct:pal("Filename: ~ts", [Filename]), ?assert(filelib:is_file(Filename)) ; ({dir, Dirname}) -> - ct:pal("Directory: ~s", [Dirname]), + ct:pal("Directory: ~ts", [Dirname]), ?assert(filelib:is_dir(Dirname)) end, Expected). diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl index 66e1fdf..45a7433 100644 --- a/test/rebar_upgrade_SUITE.erl +++ b/test/rebar_upgrade_SUITE.erl @@ -11,7 +11,8 @@ groups() -> triplet_a, triplet_b, triplet_c, tree_a, tree_b, tree_c, tree_c2, tree_cj, tree_ac, tree_all, delete_d, promote, stable_lock, fwd_lock, - compile_upgrade_parity, umbrella_config]}, + compile_upgrade_parity, umbrella_config, + profiles, profiles_exclusion]}, {git, [], [{group, all}]}, {pkg, [], [{group, all}]}]. @@ -78,6 +79,23 @@ setup_project(Case=umbrella_config, Config0, Deps, UpDeps) -> [{rebarconfig, TopConf}, {rebarumbrella, RebarConf}, {next_top_deps, rebar_test_utils:top_level_deps(UpDeps)} | Config]; +setup_project(Case, Config0, Deps, UpDeps) when Case == profiles; + Case == profiles_exclusion -> + DepsType = ?config(deps_type, Config0), + NameRoot = atom_to_list(Case)++"_"++atom_to_list(DepsType), + Config = rebar_test_utils:init_rebar_state(Config0, NameRoot++"_"), + AppDir = filename:join([?config(apps, Config), "apps", NameRoot]), + rebar_test_utils:create_app(AppDir, "Root", "0.0.0", [kernel, stdlib]), + [Top|ProfileDeps] = rebar_test_utils:top_level_deps(Deps), + RebarConf = rebar_test_utils:create_config(AppDir, [ + {deps, [Top]}, + {profiles, [{fake, [{deps, ProfileDeps}]}]} + ]), + [NextTop|NextPDeps] = rebar_test_utils:top_level_deps(UpDeps), + NextConfig = [{deps, [NextTop]}, + {profiles, [{fake, [{deps, NextPDeps}]}]}], + [{rebarconfig, RebarConf}, + {next_config, NextConfig} | Config]; setup_project(Case, Config0, Deps, UpDeps) -> DepsType = ?config(deps_type, Config0), Config = rebar_test_utils:init_rebar_state( @@ -454,7 +472,47 @@ upgrades(umbrella_config) -> {[{"A", "1", []}], [{"A", "2", []}], ["A"], - {"A", [{"A","2"}]}}. + {"A", [{"A","2"}]}}; +upgrades(profiles) -> + %% Ensure that we can unlock deps under a given profile; + %% B and C should both be in a custom profile + %% and must not be locked. + {[{"A", "1", [{"D",[]}, + {"E","3",[]}]}, + {"B", "1", [{"F","1",[]}, + {"G",[]}]}, + {"C", "0", [{"H","3",[]}, + {"I",[]}]}], + [{"A", "2", [{"D",[]}, + {"E","2",[]}]}, + {"B", "2", [{"F","2",[]}, + {"G",[]}]}, + {"C", "1", [{"H","4",[]}, + {"I",[]}]}], + ["A","B","C","E","F","H"], + {"C", [{"A","1"}, "D", {"E","3"}, + {"B","2"}, {"F","2"}, "G", + {"C","1"}, {"H","4"}, "I"]}}; +upgrades(profiles_exclusion) -> + %% Ensure that we can unlock deps under a given profile; + %% B and C should both be in a custom profile + %% and must not be locked. + {[{"A", "1", [{"D",[]}, + {"E","3",[]}]}, + {"B", "1", [{"F","1",[]}, + {"G",[]}]}, + {"C", "0", [{"H","3",[]}, + {"I",[]}]}], + [{"A", "2", [{"D",[]}, + {"E","2",[]}]}, + {"B", "2", [{"F","2",[]}, + {"G",[]}]}, + {"C", "1", [{"H","4",[]}, + {"I",[]}]}], + ["A","B","C","E","F","H"], + {"A", [{"A","1"}, "D", {"E","3"}, + {"B","2"}, {"F","2"}, "G", + {"C","1"}, {"H","4"}, "I"]}}. %% TODO: add a test that verifies that unlocking files and then %% running the upgrade code is enough to properly upgrade things. @@ -613,6 +671,37 @@ umbrella_config(Config) -> ), meck:unload(rebar_prv_upgrade). +profiles(Config) -> + apply(?config(mock, Config), []), + {ok, TopConfig} = file:consult(?config(rebarconfig, Config)), + %% Install dependencies before re-mocking for an upgrade + rebar_test_utils:run_and_check(Config, TopConfig, ["lock"], {ok, []}), + %% Install test deps along with them + rebar_test_utils:run_and_check(Config, TopConfig, ["as","fake","lock"], {ok, []}), + {App, Unlocks} = ?config(expected, Config), + ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]), + Expectation = case Unlocks of + {error, Term} -> {error, Term}; + _ -> {ok, [T || T <- Unlocks, + element(1,T) == dep orelse + lists:member(element(2,T), ["A","D","E"])]} + end, + + meck:new(rebar_prv_app_discovery, [passthrough]), + meck:expect(rebar_prv_app_discovery, do, fun(S) -> + apply(?config(mock_update, Config), []), + meck:passthrough([S]) + end), + NewRebarConf = rebar_test_utils:create_config(?config(apps, Config), + ?config(next_config, Config)), + {ok, NewRebarConfig} = file:consult(NewRebarConf), + rebar_test_utils:run_and_check( + Config, NewRebarConfig, ["as","fake","upgrade", App], Expectation + ), + meck:unload(rebar_prv_app_discovery). + +profiles_exclusion(Config) -> profiles(Config). + run(Config) -> apply(?config(mock, Config), []), ConfigPath = ?config(rebarconfig, Config), diff --git a/test/rebar_xref_SUITE.erl b/test/rebar_xref_SUITE.erl index 09f73a7..f052fa4 100644 --- a/test/rebar_xref_SUITE.erl +++ b/test/rebar_xref_SUITE.erl @@ -9,7 +9,9 @@ end_per_testcase/2, all/0, xref_test/1, - xref_ignore_test/1]). + xref_ignore_test/1, + xref_dep_hook/1, + xref_undef_behaviour/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -28,6 +30,15 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_testcase(xref_dep_hook, Config) -> + Src = filename:join([?config(data_dir, Config), "recursive"]), + Dst = filename:join([?config(priv_dir, Config), "recursive"]), + ok = rebar_file_utils:cp_r([Src], Dst), + GlobalDir = filename:join([?config(priv_dir, Config), "cache"]), + State = rebar_state:new([{base_dir, filename:join([Dst, "_build"])} + ,{global_rebar_dir, GlobalDir} + ,{root_dir, Dst}]), + [{apps, Dst}, {state, State} | Config]; init_per_testcase(Case, Config) -> UpdConfig = rebar_test_utils:init_rebar_state(Config), AppDir = ?config(apps, UpdConfig), @@ -48,7 +59,7 @@ end_per_testcase(_, _Config) -> ok. all() -> - [xref_test, xref_ignore_test]. + [xref_test, xref_ignore_test, xref_dep_hook, xref_undef_behaviour]. %% =================================================================== %% Test cases @@ -70,6 +81,21 @@ xref_ignore_test(Config) -> Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]), verify_results(xref_ignore_test, Name, Result). +xref_dep_hook(Config) -> + rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, []}). + +xref_undef_behaviour(Config) -> + AppDir = ?config(apps, Config), + State = ?config(state, Config), + Name = ?config(app_name, Config), + RebarConfig = ?config(rebar_config, Config), + %% delete one of the behaviours, which should create new warnings + delete_src_file(AppDir, Name, behaviour1), + %% just ensure this does not crash + Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]), + verify_results(xref_undef_behaviour, Name, Result). + + %% =================================================================== %% Helper functions %% =================================================================== @@ -110,6 +136,31 @@ verify_test_results(xref_test, AppName, XrefResults, _QueryResults) -> ?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)), ?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)), ok; +verify_test_results(xref_undef_behaviour, AppName, XrefResults, _QueryResults) -> + AppModules = ["behaviour2", "mymod", "othermod", "somemod"], + [Behaviour2Mod, MyMod, OtherMod, SomeMod] = + [list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules], + UndefFuns = proplists:get_value(undefined_functions, XrefResults), + UndefFunCalls = proplists:get_value(undefined_function_calls, XrefResults), + LocalsNotUsed = proplists:get_value(locals_not_used, XrefResults), + ExportsNotUsed = proplists:get_value(exports_not_used, XrefResults), + DeprecatedFuns = proplists:get_value(deprecated_functions, XrefResults), + DeprecatedFunCalls = proplists:get_value(deprecated_function_calls, XrefResults), + ?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)), + ?assert(lists:member({{OtherMod, somefunc, 0}, {SomeMod, notavailable, 1}}, + UndefFunCalls)), + ?assert(lists:member({MyMod, fdeprecated, 0}, DeprecatedFuns)), + ?assert(lists:member({{OtherMod, somefunc, 0}, {MyMod, fdeprecated, 0}}, + DeprecatedFunCalls)), + ?assert(lists:member({MyMod, localfunc2, 0}, LocalsNotUsed)), + ?assert(lists:member({Behaviour2Mod, behaviour_info, 1}, ExportsNotUsed)), + ?assert(lists:member({MyMod, other2, 1}, ExportsNotUsed)), + ?assert(lists:member({OtherMod, somefunc, 0}, ExportsNotUsed)), + ?assert(lists:member({MyMod, bh1_a, 1}, ExportsNotUsed)), + ?assert(lists:member({MyMod, bh1_b, 1}, ExportsNotUsed)), + ?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)), + ?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)), + ok; verify_test_results(xref_ignore_test, AppName, XrefResults, _QueryResults) -> AppModules = ["behaviour1", "behaviour2", "mymod", "othermod", "somemod"], [_Behaviour1Mod, _Behaviour2Mod, _MyMod, _OtherMod, SomeMod] = @@ -128,6 +179,10 @@ write_src_file(Dir, AppName, Module, IgnoreXref) -> ok = filelib:ensure_dir(Erl), ok = ec_file:write(Erl, get_module_body(Module, AppName, IgnoreXref)). +delete_src_file(Dir, AppName, Module) -> + Erl = filename:join([Dir, "src", module_name(AppName, Module)]), + ok = file:delete(Erl). + module_name(AppName, Module) -> lists:flatten([AppName, "_", atom_to_list(Module), ".erl"]). diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/rebar.config b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/rebar.config new file mode 100644 index 0000000..cf48edf --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/rebar.config @@ -0,0 +1,12 @@ +{erl_opts, [debug_info]}. +{deps, []}. + +{xref_checks,[ + undefined_function_calls, + undefined_functions, + locals_not_used, + deprecated_function_calls, + deprecated_functions +]}. + +{provider_hooks, [{post, [{compile, xref}]}]}. diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1.app.src b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1.app.src new file mode 100644 index 0000000..b935082 --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1.app.src @@ -0,0 +1,16 @@ +{application, rebar_issue1, + [{description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, { rebar_issue1_app, []}}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {maintainers, []}, + {licenses, []}, + {links, []} + ]}. diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1_app.erl b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1_app.erl new file mode 100644 index 0000000..78c88c1 --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1_app.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%% @doc rebar_issue1 public API +%% @end +%%%------------------------------------------------------------------- + +-module(rebar_issue1_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%%==================================================================== +%% API +%%==================================================================== + +start(_StartType, _StartArgs) -> + rebar_issue1_sup:start_link(). + +%%-------------------------------------------------------------------- +stop(_State) -> + ok. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1_sup.erl b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1_sup.erl new file mode 100644 index 0000000..6e5a9f8 --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue1/src/rebar_issue1_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc rebar_issue1 top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(rebar_issue1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%==================================================================== +%% API functions +%%==================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%==================================================================== +%% Supervisor callbacks +%%==================================================================== + +%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} +init([]) -> + {ok, { {one_for_all, 0, 1}, []} }. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2.app.src b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2.app.src new file mode 100644 index 0000000..59ffa35 --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2.app.src @@ -0,0 +1,17 @@ +{application, rebar_issue2, + [{description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, { rebar_issue2_app, []}}, + {applications, + [kernel, + stdlib, + rebar_issue1 + ]}, + {env,[]}, + {modules, []}, + + {maintainers, []}, + {licenses, []}, + {links, []} + ]}. diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2_app.erl b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2_app.erl new file mode 100644 index 0000000..968966c --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2_app.erl @@ -0,0 +1,26 @@ +%%%------------------------------------------------------------------- +%% @doc rebar_issue2 public API +%% @end +%%%------------------------------------------------------------------- + +-module(rebar_issue2_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%%==================================================================== +%% API +%%==================================================================== + +start(_StartType, _StartArgs) -> + rebar_issue2_sup:start_link(). + +%%-------------------------------------------------------------------- +stop(_State) -> + ok. + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2_sup.erl b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2_sup.erl new file mode 100644 index 0000000..3673548 --- /dev/null +++ b/test/rebar_xref_SUITE_data/recursive/apps/rebar_issue2/src/rebar_issue2_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc rebar_issue2 top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(rebar_issue2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%==================================================================== +%% API functions +%%==================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%==================================================================== +%% Supervisor callbacks +%%==================================================================== + +%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} +init([]) -> + {ok, { {one_for_all, 0, 1}, []} }. + +%%==================================================================== +%% Internal functions +%%==================================================================== |