pax_global_header00006660000000000000000000000064117631462630014523gustar00rootroot0000000000000052 comment=1c98f6ccd4adc915167d4302d732d79e4da3d390 rebar-2.0.0/000077500000000000000000000000001176314626300126155ustar00rootroot00000000000000rebar-2.0.0/.gitignore000066400000000000000000000001531176314626300146040ustar00rootroot00000000000000*.beam rebar *~ *.orig .*.swp rt.work .hgignore .eunit dialyzer_warnings xref_warnings rebar.cmd rebar.ps1 rebar-2.0.0/.hgignore000066400000000000000000000000661176314626300144220ustar00rootroot00000000000000\.beam ^rebar$ .~ \.orig \.swp rt.work/* ^.gitignore$ rebar-2.0.0/.hgtags000066400000000000000000000000631176314626300140720ustar00rootroot00000000000000e8747041ef63f1b394d3b156c72c5bc12e92ecc4 RELEASE-1 rebar-2.0.0/LICENSE000066400000000000000000000236771176314626300136410ustar00rootroot00000000000000 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 rebar-2.0.0/Makefile000066400000000000000000000010201176314626300142460ustar00rootroot00000000000000.PHONY: dialyzer_warnings xref_warnings all: ./bootstrap clean: @rm -rf rebar ebin/*.beam inttest/rt.work debug: @./bootstrap debug check: debug xref dialyzer xref: @./rebar xref dialyzer: dialyzer_warnings @diff -U0 dialyzer_reference dialyzer_warnings dialyzer_warnings: -@dialyzer -q -n ebin -Wunmatched_returns -Werror_handling \ -Wrace_conditions > dialyzer_warnings binary: VSN = $(shell ./rebar -V) binary: clean all cp rebar ../rebar.wiki/rebar (cd ../rebar.wiki && git commit -m "Update $(VSN)" rebar)rebar-2.0.0/NOTES.org000066400000000000000000000007271176314626300142240ustar00rootroot00000000000000 * Major operations ** Compile *** Code generation *** Compilation/linking *** App validation ** Clean ** ct testing ** eunit testing ** Installation ** Doc generation * Modes/File types ** Erlang ** Port driver ** NIF driver ** SNMP MIBs ** ASN.1 files * Misc. Notes ** Port/NIF driver compilation needs pre/post hook ** Need to support code generation for things like protobuf ** Need to support compilation flags * Contexts ** Application ** General ** Release ?! rebar-2.0.0/README.md000066400000000000000000000100141176314626300140700ustar00rootroot00000000000000rebar ===== rebar is an Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. rebar is a self-contained Erlang script, so it's easy to distribute or even embed directly in a project. Where possible, rebar uses standard Erlang/OTP conventions for project structures, thus minimizing the amount of build configuration work. rebar also provides dependency management, enabling application writers to easily re-use common libraries from a variety of locations (git, hg, etc). Building -------- Information on building and installing [Erlang/OTP](http://www.erlang.org) can be found [here](https://github.com/erlang/otp/wiki/Installation) ([more info](https://github.com/erlang/otp/blob/master/INSTALL.md)). ### Dependencies To build rebar you will need a working installation of Erlang R13B03 (or later). Should you want to clone the rebar repository, you will also require git. #### Downloading You can download a pre-built binary version of rebar from: https://github.com/basho/rebar/wiki/rebar #### Building rebar ```sh $ git clone git://github.com/basho/rebar.git $ cd rebar $ ./bootstrap Recompile: src/getopt ... Recompile: src/rebar_utils ==> rebar (compile) Congratulations! You now have a self-contained script called "rebar" in your current working directory. Place this script anywhere in your path and you can use rebar to build OTP-compliant apps. ``` Contributing to rebar ===================== Pull requests and branching --------------------------- Use one topic branch per pull request. Do not commit to master in your fork. Provide a clean branch without any merge commits from upstream. Usually you should squash any intermediate commits into the original single commit. Code style ---------- Do not introduce trailing whitespace. Do not mix spaces and tabs. Do not introduce lines longer than 80 characters. [erlang-mode (emacs)](http://www.erlang.org/doc/man/erlang.el.html) indentation is preferred. vi-only users are encouraged to give [Vim emulation](http://emacswiki.org/emacs/Evil) ([more info](https://gitorious.org/evil/pages/Home)) a try. Writing Commit Messages ----------------------- Structure your commit message like this:
One line summary (less than 50 characters)

Longer description (wrap at 72 characters)
### Summary * Less than 50 characters * What was changed * Imperative present tense (fix, add, change) * `Fix bug 123` * `Add 'foobar' command` * `Change default timeout to 123` * No period ### Description * Wrap at 72 characters * Why, explain intention and implementation approach * Present tense ### Atomicity * Break up logical changes * Make whitespace changes separately Dialyzer and Tidier ------------------- Before you submit a patch check for [xref](http://www.erlang.org/doc/man/xref.html) and [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html) warnings. A successful run of ``make check`` looks like: ```sh $ make check Recompile: src/rebar_core ==> rebar (compile) Command 'debug' not understood or not applicable Congratulations! You now have a self-contained script called "rebar" in your current working directory. Place this script anywhere in your path and you can use rebar to build OTP-compliant apps. ==> rebar (xref) make: [dialyzer_warnings] Error 2 (ignored) ``` [xref](http://www.erlang.org/doc/man/xref.html) and [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html) warnings are compared against a set of safe-to-ignore warnings found in [dialyzer_reference](https://raw.github.com/tuncer/rebar/maint/dialyzer_reference) and [xref_reference](https://raw.github.com/tuncer/rebar/maint/xref_reference). It is **strongly recommended** to check the code with [Tidier](http://tidier.softlab.ntua.gr:20000/tidier/getstarted). Select all transformation options and enable **automatic** transformation. If Tidier suggests a transformation apply the changes **manually** to the source code. Do not use the code from the tarball (*out.tgz*) as it will have white-space changes applied by Erlang's pretty-printer. rebar-2.0.0/THANKS000066400000000000000000000025501176314626300135320ustar00rootroot00000000000000The following people have contributed to rebar: Dave Smith Jon Meredith Tim Dysinger Bryan Fink Tuncer Ayaz Ian Wilkinson Juan Jose Comellas Tom Preston-Werner OJ Reeves Ruslan Babayev Ryan Tilder Kevin Smith David Reid Cliff Moon Chris Bernard Jeremy Raymond Bob Ippolito Alex Songe Andrew Thompson Russell Brown Chris Chew Klas Johansson Geoff Cant Kostis Sagonas Essien Ita Essien Manuel Duran Aguete Daniel Neri Misha Gorodnitzky Adam Kocoloski Joseph Wayne Norton Mihai Balea Matthew Batema Alexey Romanov Benjamin Nortier Magnus Klaar Anthony Ramine Charles McKnight Andrew Tunnell-Jones Joe Williams Daniel Reverri Jesper Louis Andersen Richard Jones Tim Watson Anders 'andekar' Christopher Brown Jordi Chacon Shunichi Shinohara Mickael Remond Evax Software Piotr Usewicz Anthony Molinaro Andrew Gopienko Steve Vinoski Evan Miller Jared Morrow Jan Kloetzke Mathias Meyer Steven Gravell Alexis Sellier Mattias Holmlund Tino Breddin David Nonnenmacher Anders Nygren Scott Lystig Fritchie Uwe Dauernheim Yurii Rashkovskii Alfonso De Gregorio Matt Campbell Benjamin Plee Ben Ellis Ignas Vysniauskas Anton Lavrik Jan Vincent Liwanag Przemyslaw Dabek Fabian Linzberger Smith Winston Jesse Gumm Torbjorn Tornkvist Ali Sabil Tomas Abrahamsson Francis Joanis fisher@yun.io Yurin Slava Phillip Toland Mike Lazar Loic Hoguin Ali Yakout Adam Schepis Amit Kapoor Ulf Wiger Nick Vatamaniuc rebar-2.0.0/bootstrap000077500000000000000000000112501176314626300145570ustar00rootroot00000000000000#!/usr/bin/env escript %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et main(Args) -> %% Get a string repr of build time Built = build_time(), %% Get a string repr of first matching VCS changeset VcsInfo = vcs_info([{hg, ".hg", "hg identify -i"}, {git, ".git", "git describe --always"}]), %% Check for force=1 flag to force a rebuild case lists:member("force=1", Args) of true -> rm("ebin/*.beam"); false -> rm("ebin/rebar_core.beam") end, %% Add check for debug flag DebugFlag = case lists:member("debug", Args) of true -> debug_info; false -> undefined end, %% Extract the system info of the version of OTP we use to compile rebar OtpInfo = string:strip(erlang:system_info(otp_release), both, $\n), %% Compile all src/*.erl to ebin case make:files(filelib:wildcard("src/*.erl"), [{outdir, "ebin"}, {i, "include"}, DebugFlag, {d, 'BUILD_TIME', Built}, {d, 'VCS_INFO', VcsInfo}, {d, 'OTP_INFO', OtpInfo}]) of up_to_date -> ok; error -> io:format("Failed to compile rebar files!\n"), halt(1) end, %% Make sure file:consult can parse the .app file case file:consult("ebin/rebar.app") of {ok, _} -> ok; {error, Reason} -> io:format("Invalid syntax in ebin/rebar.app: ~p\n", [Reason]), halt(1) end, %% Add ebin/ to our path true = code:add_path("ebin"), %% Run rebar to do proper .app validation and such rebar:main(["compile"] ++ Args), %% Read the contents of the files in ebin and templates; note that we %% place all the beam files at the top level of the code archive so %% that code loading works properly. Files = load_files("*", "ebin") ++ load_files("priv/templates/*", "."), case zip:create("mem", Files, [memory]) of {ok, {"mem", ZipBin}} -> %% Archive was successfully created. Prefix that binary with our %% header and write to "rebar" file. %% Without -noshell -noinput escript consumes all input that would %% otherwise go to the shell for the next command. Script = <<"#!/usr/bin/env escript\n%%! -noshell -noinput\n", ZipBin/binary>>, case file:write_file("rebar", Script) of ok -> ok; {error, WriteError} -> io:format("Failed to write rebar script: ~p\n", [WriteError]), halt(1) end; {error, ZipError} -> io:format("Failed to construct rebar script archive: ~p\n", [ZipError]), halt(1) end, %% Finally, update executable perms for our script on *nix, %% or write out script files on win32. case os:type() of {unix,_} -> [] = os:cmd("chmod u+x rebar"), ok; {win32,_} -> write_windows_scripts(), ok; _ -> ok end, %% Add a helpful message io:format("Congratulations! You now have a self-contained script called" " \"rebar\" in\n" "your current working directory. " "Place this script anywhere in your path\n" "and you can use rebar to build OTP-compliant apps.\n"). rm(Path) -> NativePath = filename:nativename(Path), Cmd = case os:type() of {unix,_} -> "rm -f "; {win32,_} -> "del /q " end, [] = os:cmd(Cmd ++ NativePath), ok. build_time() -> {{Y, M, D}, {H, Min, S}} = calendar:now_to_universal_time(now()), lists:flatten(io_lib:format("~4..0w~2..0w~2..0w_~2..0w~2..0w~2..0w", [Y, M, D, H, Min, S])). load_files(Wildcard, Dir) -> [read_file(Filename, Dir) || Filename <- filelib:wildcard(Wildcard, Dir)]. read_file(Filename, Dir) -> {ok, Bin} = file:read_file(filename:join(Dir, Filename)), {Filename, Bin}. vcs_info([]) -> "No VCS info available."; vcs_info([{Id, Dir, Cmd} | Rest]) -> case filelib:is_dir(Dir) of true -> lists:concat([Id, " ", string:strip(os:cmd(Cmd), both, $\n)]); false -> vcs_info(Rest) end. 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("rebar.cmd", CmdScript). rebar-2.0.0/bootstrap.bat000066400000000000000000000000431176314626300153170ustar00rootroot00000000000000@echo off escript.exe bootstrap %* rebar-2.0.0/dialyzer_reference000066400000000000000000000001151176314626300163760ustar00rootroot00000000000000 rebar_utils.erl:154: Call to missing or unexported function escript:foldl/3 rebar-2.0.0/ebin/000077500000000000000000000000001176314626300135325ustar00rootroot00000000000000rebar-2.0.0/ebin/rebar.app000066400000000000000000000053351176314626300153350ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {application, rebar, [{description, "Rebar: Erlang Build Tool"}, {vsn, "2.0.0"}, {modules, [ rebar, rebar_abnfc_compiler, rebar_appups, rebar_app_utils, rebar_base_compiler, rebar_config, rebar_core, rebar_cleaner, rebar_ct, rebar_deps, rebar_asn1_compiler, rebar_edoc, rebar_erlc_compiler, rebar_escripter, rebar_eunit, rebar_file_utils, rebar_lfe_compiler, rebar_erlydtl_compiler, rebar_log, rebar_otp_app, rebar_port_compiler, rebar_protobuffs_compiler, rebar_neotoma_compiler, rebar_rel_utils, rebar_reltool, rebar_require_vsn, rebar_subdirs, rebar_templater, rebar_upgrade, rebar_utils, rebar_xref, getopt, mustache ]}, {registered, []}, {applications, [kernel, stdlib, sasl]}, {env, [ %% Default log level {log_level, error}, %% Default parallel jobs {jobs, 3}, %% any_dir processing modules {any_dir_modules, [ rebar_require_vsn, rebar_deps, rebar_subdirs, rebar_templater, rebar_cleaner ]}, %% Dir specific processing modules {modules, [ {app_dir, [ rebar_abnfc_compiler, rebar_protobuffs_compiler, rebar_neotoma_compiler, rebar_asn1_compiler, rebar_erlc_compiler, rebar_lfe_compiler, rebar_erlydtl_compiler, rebar_port_compiler, rebar_otp_app, rebar_ct, rebar_eunit, rebar_escripter, rebar_edoc, rebar_xref ]}, {rel_dir, [ rebar_appups, rebar_reltool, rebar_upgrade ]} ]} ]} ]}. rebar-2.0.0/include/000077500000000000000000000000001176314626300142405ustar00rootroot00000000000000rebar-2.0.0/include/rebar.hrl000066400000000000000000000007051176314626300160440ustar00rootroot00000000000000-define(FAIL, throw({error, failed})). -define(ABORT(Str, Args), rebar_utils:abort(Str, Args)). -define(CONSOLE(Str, Args), io:format(Str, Args)). -define(DEBUG(Str, Args), rebar_log:log(debug, Str, Args)). -define(INFO(Str, Args), rebar_log:log(info, Str, Args)). -define(WARN(Str, Args), rebar_log:log(warn, Str, Args)). -define(ERROR(Str, Args), rebar_log:log(error, Str, Args)). -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))). rebar-2.0.0/inttest/000077500000000000000000000000001176314626300143075ustar00rootroot00000000000000rebar-2.0.0/inttest/bug_5_rt.erl000066400000000000000000000012271176314626300165230ustar00rootroot00000000000000-module(bug_5_rt). -compile(export_all). files() -> [{create, "ebin/a1.app", app(a1)}, {create, "deps/d1/src/d1.app.src", app(d1)}, {create, "rebar.config", <<"{deps, [{d1, \"1\", {hg, \"http://example.com\", \"tip\"}}]}.\n">>}, {copy, "../rebar", "rebar"}]. run(_Dir) -> {ok, _} = retest:sh("./rebar compile"), ok. %% %% Generate the contents of a simple .app file %% app(Name) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/ct1/000077500000000000000000000000001176314626300147765ustar00rootroot00000000000000rebar-2.0.0/inttest/ct1/ct1_rt.erl000066400000000000000000000011411176314626300166730ustar00rootroot00000000000000-module(ct1_rt). -compile(export_all). files() -> [{create, "ebin/a1.app", app(a1)}, {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "test_SUITE.erl", "itest/test_SUITE.erl"}]. run(_Dir) -> {ok, _} = retest:sh("./rebar compile ct"), ok. %% %% Generate the contents of a simple .app file %% app(Name) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/ct1/rebar.config000066400000000000000000000000241176314626300172540ustar00rootroot00000000000000 {ct_dir, "itest"}. rebar-2.0.0/inttest/ct1/test_SUITE.erl000066400000000000000000000002411176314626300174270ustar00rootroot00000000000000-module(test_SUITE). -compile(export_all). -include_lib("ct.hrl"). all() -> [simple_test]. simple_test(Config) -> io:format("Test: ~p\n", [Config]). rebar-2.0.0/inttest/ct2/000077500000000000000000000000001176314626300147775ustar00rootroot00000000000000rebar-2.0.0/inttest/ct2/ct2_rt.erl000066400000000000000000000011501176314626300166750ustar00rootroot00000000000000-module(ct2_rt). -compile(export_all). files() -> [{create, "ebin/foo.app", app(foo)}, {copy, "../../rebar", "rebar"}, {copy, "foo.test.spec", "test/foo.test.spec"}, {copy, "foo_SUITE.erl", "test/foo_SUITE.erl"}]. run(_Dir) -> {ok, _} = retest:sh("./rebar compile ct -v"), ok. %% %% Generate the contents of a simple .app file %% app(Name) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/ct2/foo.test.spec000066400000000000000000000000271176314626300174130ustar00rootroot00000000000000{suites, "test", all}. rebar-2.0.0/inttest/ct2/foo_SUITE.erl000066400000000000000000000002401176314626300172330ustar00rootroot00000000000000-module(foo_SUITE). -include_lib("common_test/include/ct.hrl"). -compile(export_all). all() -> [foo]. foo(Config) -> io:format("Test: ~p\n", [Config]). rebar-2.0.0/inttest/retest000077500000000000000000000410751176314626300155520ustar00rootroot00000000000000#!/usr/bin/env escript PK!W<\ getopt.beamX}l[uOO G['~L$Be˹Eqb&i#RD"GqN&'q#M)b  ],K l>aav`l_c ld*:<;ęOJRO~*3ؤS_$B$I ,XNŦmfX[X,lY-Ub~o5Z;شNY/O-gW^X.,Vjйlb٪J*oԠZ) -JEۮ,@h\*Flڢo/֛i[5åsz*HtT`f_C6wxba#l܊2g5w5lz} ŚVi9۰NkjԊͳ"ʵ> 1!A7䶍rMi+Аػja#P |E]|oRכ+E?_jq6tRaI3-Ydۋ/ZFjr MP*:VdU`yjVVBha$94ߪ ŇtlIqT[.{uYߦ닑B%o00S҂:ɌuLN4./j%ҭkrv)H3ei㖦OiRbJS1+q>eǭ}.WniV^d:W$7/>;>0y߻9|`">&Έ"3#A=]3}&щ֭j:+@uN_p(KhL#IVf<̏")[ڧ[ƋHn%`y+ #jץ!il:͒ğ} ߈"U]qKhgV:°o3/7)1"R.#eV",ai$d4>ޝ]dgRB&6Y$ǒ d V&UX`(//H4hN(x.Fsn}=Ye8* a;|0!`}OA⩆43s=oji*;ndUVvWWVfFލYH?rQN437^$מ@O t$OhO:Խ~{O[gEQ Ij,TP4)fesj 9Ten~wݲrgeeODDXY큸+z60 VK{4Jd.3 $.McKD9~`5ЎjX l[{ ŞiM0Z -D&HQ(o":Zd4[dm&87iAl]X0feetz(I M %9FDŽrmF@sz!c Vӄ?!0 `zǕ̺DhF z@VZY9,f0.^IYK/#8Ǝh W$E)z6tu'@!0/2l j<·I mq~ J,FÌGRHh [vjh2r^]omw;Uİa'@S);x8"L>FXt&5C=E x\HVL!+gr%nʱm*A&O裸H{{P3qz{:BmG[Ryuy**B?ka`OS&{2Fs'f\1zLNS̊S141 b]~ N31b \gcļ.v_ѯ#Z>Gb\3t~C]潺?2PQF ,Y)JGȊQ2/,t),n+f%t\ai/J]r_v[N_$WhwU3hM3/yU"{_h?-؄b?(+uaY=#H񌴒aP:ޛ76kxʐ( 3\l8xԢ__#ij65 D< $noQ:7_ȚɹwD/!`w/K^&KF6G\Ts%O@P ]ŬVW tat&&d726/ :< |"琔CL.20yŒ[tiE.BU,zʫ(/ג%ᤣ#.kaI̼w/~}%/po|toS 7BE!+ol5Cy3F/V37Mǧs9!)+/mMW}qz7:2|+I/':z\9HjX]j|ѯ*;vίwԴ~MYSoRՋZT|_}Kj4 (KͮHGf2DuԽ-EdtoH]Ѽ"n; O*&<^/V8tVRx (軬)OV9P.L]%+AKk׈x< B4-?n,ۓKc::BAwy*&%x|dnm:^q̷!?ɿ}TͿp}3wo˗{sߛw:'KNW3b?O ?_Sٿב7%v?Aߣ8#G.\b27z'>~H?H%g]_aJ6+XGXBtH_4/籒^Jz![gFVTzc+<={>>w =G)r-hn߉.B^x+ U](K]JЩ?\;nk/q@,֛LYҖZR+VZͅ%)}6v}\-(~dȕe)Xڭr,J,Ѧ崚Yh,mfR[`WC]9TVE1Pbua[͒, 8R,4ZsJiN,q9V$MPKY9<Ȳ  mustache.beamX p߽{[w{!d $ {y\.`r>:&9r;K:Te|`f2"VSTNNEjgZd*m:PDѪ텫`m}.tM )w;ohRELQf8"AM QD8$M'L!;#jF)үae}Zݑ3 œ1ΘҬbb%U{"!\b|jyDkw%%z Ebe#apNN#Ql(DVp D3S\`Uybhr |]i!S N{ZC!X9xGZe2.20,+̲wTe}l jYjEAhd6"Pc 78(8u?ΝeGǫE!5cуǪ./>сτl}gS¥q je\9%ʸZ!]7)b.TuT:8SW`6Kl9aѻErG:͍p5H "\6:)tRh(r[ )[F00r[!aH~ ԱwJ=!W±m 1.HެT Dmb"!M4HR&cx˥9 i"~\˳<[hؘu7pk&8>V( Ch[.24]ȱLd&"2Y]'K7\@vl?@;-2(A8P@[ Nlv)txj'|jD8 6;\`4—S\eRt邔[ly ) 4L= 7CWbnfn Mv@jM 43 ބw@aC)_l|6N*5~t{A3JCNFbS-ywNA P`#r/,hț {[U,7=dԆ|wc}͵6soxC֕g/&ǻG%?`_ܹ'mox] Ӟ.~o|tU68?|W/-~E?pnۗ=҅,pS7lh6?xhb<m_+Ȍy׽kGDEvȼ7CW[ 'ՙS޿䦇?ش?\>0jig[Ն-VY7n;GW .;|&Q+SQʹ' o< 4ϰ'n4t!'{횦}frM1 ݝI:QMJM^biZb`d:DCaGU2Z*VEM*@W㙉-&?]d&t|œD6RY-xf*DedX1&u5I~2ғ_2 e77Y+>8>Fl6%2U( 86{O!FپD<@!$W&֒ߗPK`FRV3#LpߵNe6Ӣ PU,Y~s恶#7, ߷Z7 d3N-ɇzypfX57_m%Hn7ISM(PfCNPKF<^ retest.beamMS=l@;6n)E$EU6 i3ǎ}I8vtvJ*1b`6v?*bdbdιZ|w=|+W [(fJ"Frr%#Ҁ* Y~SnY^rЮ5*62jdGH, fwdu7&jZ6o O"Cb]nEi*W%GngXa٦i:Y6ɯJW߿pKdg~ca}!l4*}>H}`3wyBQ?xr1xÍ1p_?qrm̵k~?2O Hµ#ǥ ; >k 0) q=BCVF33Kܓ#KZJOKքh%aЧ6LY=8xdCjg{Ͱ zsPK!W<9ʹRretest_config.beamUTME힝]'KFv&vEd2dY LLz$KYCX,BrY""hI<)Vʹ:6|{_}ޫW>œԗZbZ 1,Aqv- Bƽ!3"lbq,bC ,㲐Ne&S^[r:>VǤ5xQOۊ`|s:"Lkf Sf8x%LX63~wŬN[W8`Al8/cκ.X/61ȴ1^l&rW +kMihpkfyM)-ZyLqn"2gF[gkQl%021U*1(]mC_:=t{>P']!Z:D5. T)%".bkWCW}h'RoPKؑ`N 6\<%GӹlDZdS*@!M8txf4 x&!ٱd:ݔ;xN#4`#N@dS =+r6ˬ&BteFDT&M,ܖXŽG|<+;BWB+؋oUn\z{9){zC&n*Si@KBVrPahZ%QFM2|}HpeYձ nMC<>M(A"Q$LcC(xᔕrce$~wmH  &59pHV*s vV)k`d 8&{"#t*՞^9W1`0w ew@OKaʊ؏HJUbmn^D͈1Wws]/1!s0 ѬP,u`Yv pfKA*vH b(b[s4;C{XK00Wp mHškZHBk!0] wzZ<|g_1|ͮy>G>G̶=!'?,y$ }` s=|+Cg*"hǪ;HbLqhk}^L~t1ӥ?@lcvF1C&$LPBN;'?GCgmi6$l m mi/UpDd<4D HW+)~d U'G7iv×_R_Nug}x?V3AّBlen{pB7ŅYmYU41̏~%7 $m x|5C(F'G!OBB"t&/֘]M C S oPtjqz K`0p)y^l?dL̶`Lу!OS,a2~8Y"%{02tln)c%\c=Й/4U@_Tm/C_nCW\i*O|`]_Xૹ 斂56r'kA݂ (To7:uGn*m'׀tgA%kSpwְ!0T?71l1tSΤ:I~V_q3HE|ឳy":^|mW7q}c?1u'X6o7v{>yh{ƺO= mbCc?3O\9)n:8ם͗OVyu|ݾ#뎽{GμUvnl?6'/)_x_~?|*'G}x窵[ԛp|k?^{}TC~ 7>JcDǮԭ_J.yS8}\+?vSsu˯^IPKS JXTI'6*fG(wŚ ђVK  D-ђ(m]G(O݀ ]v&T%^Rx]Ӑ)BVRZcFPK!W'|Lwh=)W͜\r&&9+¬Af tP֤˖.̘0;NMtٰaNPDT΢HZ$t{R5i MK$DP~\ %?'|%umՋ+omj5V{~ڬl?\o݋O˹~}5f8~VyC<,&G\EBG_U740}ō7ܺn[ א+~ԣ?&/hTqztV"x|=Td":=e:N9y,NCyz7oC(\3VyVdTaPKx<]0t retest_sh.beammVml(̈́Nhl(v$f|:`V&i[L 8Ŋ hQG`)i d:o Œݺ5X ǰYGѩNC==܉FR ào2 3پ]K%՝W˕Z|KI [eJZ({¨ml;'"w7beT)\ 8P1|vFcr4uu+QQcUNĴbW1j;* r+Ʋ]}U "n +G%R٫Uۄq۱hftWV <۰JղZoګeXoJWm+j{׭ +UQ˞/!Wq13L3{ ۫٭  lim΂^ֆ+ֆe3LGd"@@b$1 #y V3 J+k"Iڍ(F%:#e95/23Fv"pZ\$KHi5]{N·OG S.>>Q%T2M4-+:;2-gtCONemOqJziPȊB7ȏ3*|H:ߣ'uU6{V9(s$BFD|lE>7 L XT XΤnH|(t=$4 .e ݄Z21L' q>/#i0 h I< g`bJHfR QI(mC#L鄔 ST %I3 C&Rg)l>*PP+bQ2{3BMozU EfP͔7n"f/s32lNl.] mϭ n;m Nqj8ĩT*N 8qT*NM *@(>f#)Q>#~)a6M@%#n[7}ni}G&DJDNafCvYU;j @кaID-e`cbgN`JHQ#$YwEM lOH ˇӀ iY3 wy.a'F3 . \PDR "nSF\90"lD #v#n \IY8> 2LhHNU"I1qℐ'>=%LHi* h aL^5& brGG{]ӧ4u MF& $\;>C8kIIK n?@#qK&rA$CY= ^Lߘ Bc) >u Qp gx>G\ lc"!Zma[l0^ y֣N@WS;Oְۛί+a0`$ju86աj^ 2M?{a@zC au_*t[0Գ}ϼηL^%/}ﵗ7k׮Y=X}7Agÿ]?gW'[|G=3 ~rl)9mz/|[\"O9_]}>} x?K;?]{}hEJf՞^5{^_.yէVծ#9}_}~?tЏ?GЛ0{L2K'jNqDF^/f}- *gi|ܭeש'PyRlڏu kϗ甂[b6甝*Ae&=2N9/KyވɈ)jn3ef4H VNYgJL7.s =W `PKY9<^a4`retest_utils.beamU]lU3w3l;tvgK-SR.>Lw] 5ݙN;Rl04|PP) Ac|P@+Oh!kQHHN&Sctךvͯ{lٱ=2nEӮ8e= "e@>UZ@3%ѵZ`qJ 2,CbFq$T:SF[$Iԯ2e5CJ]Aj*ܧ13s 0epB,'9/% rIY"}$1ߖ:͋ƜZ0') `gHފSt4,HԇyV*NtrD-_ QpzAԒ^QfDi,$dEXȨ/#Ks9a+Y@pS0+sB Q`n@.DqB4AB>"⡔|DHOE +7%[,. )kkŭjprvq %`Y'$xAq c|\di<7FCGE5Htrv YꮟQA L%{dmh*$1Xn;M0]t)@u "`.Gk}13@B|` *mt>x&ɜC2S%2 '=9ϱ@1 `^&ۻ~$__` {mO|~{r ͗@19]G>t#iמ[߭~֟緬\޿87x톰ckmS϶N{);:)~v?/܎7Mw<3Տ?Ro_%?}̝o~uG6|.Zۆ}߅sL:{<1XooL]W N7|_AS5U 2nrnS1J]F<\Bu.6OGxFsmӮzYNЦC{Ic5^ kF ϩec mJ 0/ -'jewofUnI`>\|PK!W<\ getopt.beamPKY9<Ȳ  9mustache.beamPK`F [ {copy, "reltool.config"}, {copy, "test.config"}, {copy, "vars.config"}, {copy, "../../rebar"} ]. run(_Dir) -> {ok, _} = retest_sh:run("./rebar -v generate", []), true = filelib:is_dir("mytarget"), ok. rebar-2.0.0/inttest/rgen1/test.config000066400000000000000000000000331176314626300174650ustar00rootroot00000000000000{web_port, {{web_port}} }. rebar-2.0.0/inttest/rgen1/vars.config000066400000000000000000000000221176314626300174570ustar00rootroot00000000000000{web_port, 1234}. rebar-2.0.0/inttest/t_custom_config/000077500000000000000000000000001176314626300174715ustar00rootroot00000000000000rebar-2.0.0/inttest/t_custom_config/custom.config000066400000000000000000000000771176314626300221760ustar00rootroot00000000000000 {deps, [ {boo, "."} ]}. {erl_opts, [warnings_as_errors]}. rebar-2.0.0/inttest/t_custom_config/custom_config.erl000066400000000000000000000001021176314626300230250ustar00rootroot00000000000000-module(custom_config). -compile(export_all). test() -> ok. rebar-2.0.0/inttest/t_custom_config/t_custom_config_rt.erl000066400000000000000000000023471176314626300240720ustar00rootroot00000000000000-module(t_custom_config_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {copy, "custom.config", "custom.config"}, {create, "ebin/custom_config.app", app(custom_config, [custom_config])}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), Ref = retest:sh("./rebar -C custom.config check-deps -v", [{async, true}]), {ok, Captured} = retest:sh_expect(Ref, "DEBUG: Consult config file .*/custom.config.*", [{capture, all, list}]), {ok, Missing} = retest:sh_expect(Ref, "DEBUG: Missing deps : \\[\\{dep,bad_name," "boo,\"\\.\",undefined\\}\\]", [{capture, all, list}]), retest_log:log(debug, "[CAPTURED]: ~s~n", [Captured]), retest_log:log(debug, "[Missing]: ~s~n", [Missing]), ok. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/tdeps1/000077500000000000000000000000001176314626300155075ustar00rootroot00000000000000rebar-2.0.0/inttest/tdeps1/a.erl000066400000000000000000000001631176314626300164330ustar00rootroot00000000000000-module(a). -compile(export_all). -include_lib("b/include/b.hrl"). hello() -> io:format("~s\n", [?HELLO]). rebar-2.0.0/inttest/tdeps1/a.rebar.config000066400000000000000000000000571176314626300202120ustar00rootroot00000000000000{deps, [{b, "1", {hg, "../repo/b", "tip"}}]}. rebar-2.0.0/inttest/tdeps1/b.hrl000066400000000000000000000000431176314626300164340ustar00rootroot00000000000000 -include_lib("c/include/c.hrl"). rebar-2.0.0/inttest/tdeps1/b.rebar.config000066400000000000000000000000571176314626300202130ustar00rootroot00000000000000{deps, [{c, "1", {hg, "../repo/c", "tip"}}]}. rebar-2.0.0/inttest/tdeps1/c.hrl000066400000000000000000000000301176314626300164310ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.0.0/inttest/tdeps1/tdeps1_rt.erl000066400000000000000000000025541176314626300201260ustar00rootroot00000000000000-module(tdeps1_rt). -compile(export_all). %% Exercise transitive dependencies %% A -> B -> C, where A includes a .hrl from B which includes .hrl from C files() -> [ %% A application {create, "ebin/a.app", app(a, [a])}, {copy, "a.rebar.config", "rebar.config"}, {copy, "a.erl", "src/a.erl"}, {copy, "../../rebar", "rebar"}, %% B application {create, "repo/b/ebin/b.app", app(b, [])}, {copy, "b.rebar.config", "repo/b/rebar.config"}, {copy, "b.hrl", "repo/b/include/b.hrl"}, %% C application {create, "repo/c/ebin/c.app", app(c, [])}, {copy, "c.hrl", "repo/c/include/c.hrl"} ]. run(_Dir) -> %% Initialize the b/c apps as mercurial repos so that dependencies pull %% properly HgCmd = "/bin/sh -c \"hg init && hg add && hg commit -m 'Initial commit'\"", {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/b"}]), {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/c"}]), {ok, _} = retest_sh:run("./rebar get-deps compile", []), true = filelib:is_regular("ebin/a.beam"), ok. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/tdeps2/000077500000000000000000000000001176314626300155105ustar00rootroot00000000000000rebar-2.0.0/inttest/tdeps2/a.erl000066400000000000000000000000701176314626300164310ustar00rootroot00000000000000-module({{module}}). -include_lib("b/include/b.hrl"). rebar-2.0.0/inttest/tdeps2/a.rebar.config000066400000000000000000000000571176314626300202130ustar00rootroot00000000000000{deps, [{b, "1", {hg, "../repo/b", "tip"}}]}. rebar-2.0.0/inttest/tdeps2/b.hrl000066400000000000000000000000431176314626300164350ustar00rootroot00000000000000 -include_lib("c/include/c.hrl"). rebar-2.0.0/inttest/tdeps2/b.rebar.config000066400000000000000000000000571176314626300202140ustar00rootroot00000000000000{deps, [{c, "1", {hg, "../repo/c", "tip"}}]}. rebar-2.0.0/inttest/tdeps2/c.hrl000066400000000000000000000000301176314626300164320ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.0.0/inttest/tdeps2/root.rebar.config000066400000000000000000000000601176314626300207500ustar00rootroot00000000000000{sub_dirs, ["apps/a1", "apps/a2"]}. rebar-2.0.0/inttest/tdeps2/tdeps2_rt.erl000066400000000000000000000033101176314626300201170ustar00rootroot00000000000000-module(tdeps2_rt). -compile(export_all). %% Exercise transitive dependencies where there are multiple files %% depending on the same set of deps %% [A1, A2] -> B -> C ; A1 and A2 includes B.hrl which includes C.hrl files() -> [ %% A1 application {create, "apps/a1/ebin/a1.app", app(a1, [a1])}, {copy, "a.rebar.config", "apps/a1/rebar.config"}, {template, "a.erl", "apps/a1/src/a1.erl", dict:from_list([{module, a1}])}, %% A2 application {create, "apps/a2/ebin/a2.app", app(a2, [a2])}, {copy, "a.rebar.config", "apps/a2/rebar.config"}, {template, "a.erl", "apps/a2/src/a2.erl", dict:from_list([{module, a2}])}, {copy, "root.rebar.config", "rebar.config"}, {copy, "../../rebar", "rebar"}, %% B application {create, "repo/b/ebin/b.app", app(b, [])}, {copy, "b.rebar.config", "repo/b/rebar.config"}, {copy, "b.hrl", "repo/b/include/b.hrl"}, %% C application {create, "repo/c/ebin/c.app", app(c, [])}, {copy, "c.hrl", "repo/c/include/c.hrl"} ]. run(_Dir) -> %% Initialize the b/c apps as mercurial repos so that dependencies pull %% properly HgCmd = "/bin/sh -c \"hg init && hg add && hg commit -m 'Initial commit'\"", {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/b"}]), {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/c"}]), {ok, _} = retest_sh:run("./rebar -v get-deps compile", []), ok. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/thooks/000077500000000000000000000000001176314626300156165ustar00rootroot00000000000000rebar-2.0.0/inttest/thooks/fish.erl000066400000000000000000000000671176314626300172560ustar00rootroot00000000000000-module(fish). -compile(export_all). fish() -> fish. rebar-2.0.0/inttest/thooks/rebar.config000066400000000000000000000004221176314626300200760ustar00rootroot00000000000000%% pre-scripts {pre_hooks, [{clean, "echo preclean >> preclean.out"}, {compile, "echo precompile >> precompile.out"}]}. %% post-scripts {post_hooks, [{clean, "echo postclean >> postclean.out"}, {compile, "echo postcompile >> postcompile.out"}]}. rebar-2.0.0/inttest/thooks/thooks_rt.erl000066400000000000000000000022531176314626300203400ustar00rootroot00000000000000-module(thooks_rt). -include_lib("eunit/include/eunit.hrl"). -compile(export_all). files() -> [ %% dummy lfe files {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "fish.erl", "src/fish.erl"}, {create, "ebin/fish.app", app(fish, [fish])} ]. run(_Dir) -> ?assertMatch({ok, _}, retest_sh:run("./rebar -v clean compile", [])), ensure_command_ran_only_once("preclean"), ensure_command_ran_only_once("precompile"), ensure_command_ran_only_once("postclean"), ensure_command_ran_only_once("postcompile"), ok. ensure_command_ran_only_once(Command) -> File = Command ++ ".out", ?assert(filelib:is_regular(File)), %% ensure that this command only ran once (not for each module) {ok, Content} = file:read_file(File), ?assertEqual(Command ++ "\n", binary_to_list(Content)). %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/inttest/tplugins/000077500000000000000000000000001176314626300161545ustar00rootroot00000000000000rebar-2.0.0/inttest/tplugins/bad.config000066400000000000000000000000661176314626300200730ustar00rootroot00000000000000{plugins, [bad_plugin]}. {plugin_dir, "bad_plugins"}. rebar-2.0.0/inttest/tplugins/bad_plugin.erl000066400000000000000000000002341176314626300207630ustar00rootroot00000000000000-module(bad_plugin). -compile(export_all). %% this plugin contains numerous DELIBERATE syntax errors fwibble(Config, _) > file:delete("fwibble.test") rebar-2.0.0/inttest/tplugins/fish.erl000066400000000000000000000000671176314626300176140ustar00rootroot00000000000000-module(fish). -compile(export_all). fish() -> fish. rebar-2.0.0/inttest/tplugins/rebar.config000066400000000000000000000000321176314626300204310ustar00rootroot00000000000000{plugins, [test_plugin]}. rebar-2.0.0/inttest/tplugins/test_plugin.erl000066400000000000000000000004321176314626300212140ustar00rootroot00000000000000-module(test_plugin). -compile(export_all). fwibble(Config, _) -> Pwd = rebar_utils:get_cwd(), Ok = filelib:is_regular(filename:join(Pwd, "fwibble.test")), rebar_log:log(info, "~p:~p in ~s :: ~p~n", [test_plugin, clean, Pwd, Ok]), ok = file:delete("fwibble.test"). rebar-2.0.0/inttest/tplugins/tplugins_rt.erl000066400000000000000000000024361176314626300212370ustar00rootroot00000000000000-module(tplugins_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). -define(COMPILE_ERROR, "ERROR: Plugin bad_plugin contains compilation errors:"). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "bad.config", "bad.config"}, {copy, "fish.erl", "src/fish.erl"}, {copy, "test_plugin.erl", "plugins/test_plugin.erl"}, {copy, "bad_plugin.erl", "bad_plugins/bad_plugin.erl"}, {create, "fwibble.test", <<"fwibble">>}, {create, "ebin/fish.app", app(fish, [fish])} ]. run(Dir) -> ?assertMatch({ok, _}, retest_sh:run("./rebar fwibble -v", [])), ?assertEqual(false, filelib:is_regular("fwibble.test")), Ref = retest:sh("./rebar -C bad.config -v clean", [{async, true}]), {ok, _} = retest:sh_expect(Ref, "ERROR: Plugin .*bad_plugin.erl " "contains compilation errors:.*", [{newline, any}]), ok. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.0.0/priv/000077500000000000000000000000001176314626300135755ustar00rootroot00000000000000rebar-2.0.0/priv/shell-completion/000077500000000000000000000000001176314626300170535ustar00rootroot00000000000000rebar-2.0.0/priv/shell-completion/bash/000077500000000000000000000000001176314626300177705ustar00rootroot00000000000000rebar-2.0.0/priv/shell-completion/bash/rebar000066400000000000000000000024331176314626300210100ustar00rootroot00000000000000# bash completion for rebar _rebar() { local cur prev sopts lopts cmdsnvars COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" sopts="-h -c -v -V -f -j" lopts=" --help --commands --verbose --force --jobs= --version" cmdsnvars="check-deps clean compile create create-app create-node ct \ doc delete-deps escriptize eunit get-deps generate generate-upgrade \ help list-deps list-templates update-deps version xref overlay \ apps= case= force=1 jobs= suites= verbose=1 appid= previous_release= \ nodeid= root_dir= skip_deps=true skip_apps= template= template_dir=" if [[ ${cur} == --* ]] ; then COMPREPLY=( $(compgen -W "${lopts}" -- ${cur}) ) elif [[ ${cur} == -* ]] ; then COMPREPLY=( $(compgen -W "${sopts}" -- ${cur}) ) else COMPREPLY=( $(compgen -W "${cmdsnvars}" -- ${cur}) ) fi if [ -n "$COMPREPLY" ] ; then # append space if matched COMPREPLY="${COMPREPLY} " # remove trailing space after equal sign COMPREPLY=${COMPREPLY/%= /=} fi return 0 } complete -o nospace -F _rebar rebar # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=sh rebar-2.0.0/priv/templates/000077500000000000000000000000001176314626300155735ustar00rootroot00000000000000rebar-2.0.0/priv/templates/basicnif.c000066400000000000000000000036261176314626300175240ustar00rootroot00000000000000#include "erl_nif.h" static ErlNifResourceType* {{module}}_RESOURCE = NULL; typedef struct { } {{module}}_handle; // Prototypes static ERL_NIF_TERM {{module}}_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM {{module}}_myfunction(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"new", 0, {{module}}_new}, {"myfunction", 1, {{module}}_myfunction} }; static ERL_NIF_TERM {{module}}_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { {{module}}_handle* handle = enif_alloc_resource({{module}}_RESOURCE, sizeof({{module}}_handle)); ERL_NIF_TERM result = enif_make_resource(env, handle); enif_release_resource(handle); return enif_make_tuple2(env, enif_make_atom(env, "ok"), result); } static ERL_NIF_TERM {{module}}_myfunction(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { return enif_make_atom(env, "ok"); } static void {{module}}_resource_cleanup(ErlNifEnv* env, void* arg) { /* Delete any dynamically allocated memory stored in {{module}}_handle */ /* {{module}}_handle* handle = ({{module}}_handle*)arg; */ } static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; ErlNifResourceType* rt = enif_open_resource_type(env, NULL, "{{module}}_resource", &{{module}}_resource_cleanup, flags, NULL); if (rt == NULL) return -1; {{module}}_RESOURCE = rt; return 0; } ERL_NIF_INIT({{module}}, nif_funcs, &on_load, NULL, NULL, NULL); rebar-2.0.0/priv/templates/basicnif.erl000066400000000000000000000017571176314626300200670ustar00rootroot00000000000000-module({{module}}). -export([new/0, myfunction/1]). -on_load(init/0). -define(nif_stub, nif_stub_error(?LINE)). nif_stub_error(Line) -> erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. init() -> PrivDir = case code:priv_dir(?MODULE) of {error, bad_name} -> EbinDir = filename:dirname(code:which(?MODULE)), AppPath = filename:dirname(EbinDir), filename:join(AppPath, "priv"); Path -> Path end, erlang:load_nif(filename:join(PrivDir, ?MODULE), 0). new() -> ?nif_stub. myfunction(_Ref) -> ?nif_stub. %% =================================================================== %% EUnit tests %% =================================================================== -ifdef(TEST). basic_test() -> {ok, Ref} = new(), ?assertEqual(ok, myfunction(Ref)). -endif. rebar-2.0.0/priv/templates/basicnif.template000066400000000000000000000002071176314626300211050ustar00rootroot00000000000000{variables, [{module, "mymodule"}]}. {template, "basicnif.erl", "src/{{module}}.erl"}. {template, "basicnif.c", "c_src/{{module}}.c"}. rebar-2.0.0/priv/templates/ctsuite.erl000066400000000000000000000142021176314626300177560ustar00rootroot00000000000000%% common_test suite for {{testmod}} -module({{testmod}}_SUITE). -include_lib("common_test/include/ct.hrl"). -compile(export_all). %%-------------------------------------------------------------------- %% Function: suite() -> Info %% %% Info = [tuple()] %% List of key/value pairs. %% %% Description: Returns list of tuples to set default properties %% for the suite. %% %% Note: The suite/0 function is only meant to be used to return %% default data values, not perform any other operations. %%-------------------------------------------------------------------- suite() -> [{timetrap, {seconds, 20}}]. %%-------------------------------------------------------------------- %% Function: groups() -> [Group] %% %% Group = {GroupName,Properties,GroupsAndTestCases} %% GroupName = atom() %% The name of the group. %% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] %% Group properties that may be combined. %% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] %% TestCase = atom() %% The name of a test case. %% Shuffle = shuffle | {shuffle,Seed} %% To get cases executed in random order. %% Seed = {integer(),integer(),integer()} %% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | %% repeat_until_any_ok | repeat_until_any_fail %% To get execution of cases repeated. %% N = integer() | forever %% %% Description: Returns a list of test case group definitions. %%-------------------------------------------------------------------- groups() -> []. %%-------------------------------------------------------------------- %% Function: all() -> GroupsAndTestCases %% %% GroupsAndTestCases = [{group,GroupName} | TestCase] %% GroupName = atom() %% Name of a test case group. %% TestCase = atom() %% Name of a test case. %% %% Description: Returns the list of groups and test cases that %% are to be executed. %% %% NB: By default, we export all 1-arity user defined functions %%-------------------------------------------------------------------- all() -> [ {exports, Functions} | _ ] = ?MODULE:module_info(), [ FName || {FName, _} <- lists:filter( fun ({module_info,_}) -> false; ({all,_}) -> false; ({init_per_suite,1}) -> false; ({end_per_suite,1}) -> false; ({_,1}) -> true; ({_,_}) -> false end, Functions)]. %%-------------------------------------------------------------------- %% Function: init_per_suite(Config0) -> %% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} %% %% Config0 = Config1 = [tuple()] %% A list of key/value pairs, holding the test case configuration. %% Reason = term() %% The reason for skipping the suite. %% %% Description: Initialization before the suite. %% %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> Config. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config0) -> void() | {save_config,Config1} %% %% Config0 = Config1 = [tuple()] %% A list of key/value pairs, holding the test case configuration. %% %% Description: Cleanup after the suite. %%-------------------------------------------------------------------- end_per_suite(_Config) -> ok. %%-------------------------------------------------------------------- %% Function: init_per_group(GroupName, Config0) -> %% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} %% %% GroupName = atom() %% Name of the test case group that is about to run. %% Config0 = Config1 = [tuple()] %% A list of key/value pairs, holding configuration data for the group. %% Reason = term() %% The reason for skipping all test cases and subgroups in the group. %% %% Description: Initialization before each test case group. %%-------------------------------------------------------------------- init_per_group(_group, Config) -> Config. %%-------------------------------------------------------------------- %% Function: end_per_group(GroupName, Config0) -> %% void() | {save_config,Config1} %% %% GroupName = atom() %% Name of the test case group that is finished. %% Config0 = Config1 = [tuple()] %% A list of key/value pairs, holding configuration data for the group. %% %% Description: Cleanup after each test case group. %%-------------------------------------------------------------------- end_per_group(_group, Config) -> Config. %%-------------------------------------------------------------------- %% Function: init_per_testcase(TestCase, Config0) -> %% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} %% %% TestCase = atom() %% Name of the test case that is about to run. %% Config0 = Config1 = [tuple()] %% A list of key/value pairs, holding the test case configuration. %% Reason = term() %% The reason for skipping the test case. %% %% Description: Initialization before each test case. %% %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_testcase(TestCase, Config) -> Config. %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config0) -> %% void() | {save_config,Config1} | {fail,Reason} %% %% TestCase = atom() %% Name of the test case that is finished. %% Config0 = Config1 = [tuple()] %% A list of key/value pairs, holding the test case configuration. %% Reason = term() %% The reason for failing the test case. %% %% Description: Cleanup after each test case. %%-------------------------------------------------------------------- end_per_testcase(TestCase, Config) -> Config. test_{{testmod}}() -> [{userdata,[{doc,"Testing the {{testmod}} module"}]}]. test_{{testmod}}(_Config) -> {skip,"Not implemented."}. rebar-2.0.0/priv/templates/ctsuite.template000066400000000000000000000001371176314626300210110ustar00rootroot00000000000000{variables, [{testmod, "mymodule"}]}. {template, "ctsuite.erl", "test/{{testmod}}_SUITE.erl"}. rebar-2.0.0/priv/templates/simpleapp.app.src000066400000000000000000000003361176314626300210570ustar00rootroot00000000000000{application, {{appid}}, [ {description, ""}, {vsn, "1"}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, { {{appid}}_app, []}}, {env, []} ]}. rebar-2.0.0/priv/templates/simpleapp.template000066400000000000000000000003171176314626300213230ustar00rootroot00000000000000{variables, [{appid, "myapp"}]}. {template, "simpleapp.app.src", "src/{{appid}}.app.src"}. {template, "simpleapp_app.erl", "src/{{appid}}_app.erl"}. {template, "simpleapp_sup.erl", "src/{{appid}}_sup.erl"}. rebar-2.0.0/priv/templates/simpleapp_app.erl000066400000000000000000000005531176314626300211340ustar00rootroot00000000000000-module({{appid}}_app). -behaviour(application). %% Application callbacks -export([start/2, stop/1]). %% =================================================================== %% Application callbacks %% =================================================================== start(_StartType, _StartArgs) -> {{appid}}_sup:start_link(). stop(_State) -> ok. rebar-2.0.0/priv/templates/simpleapp_sup.erl000066400000000000000000000013141176314626300211570ustar00rootroot00000000000000 -module({{appid}}_sup). -behaviour(supervisor). %% API -export([start_link/0]). %% Supervisor callbacks -export([init/1]). %% Helper macro for declaring children of supervisor -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). %% =================================================================== %% API functions %% =================================================================== start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). %% =================================================================== %% Supervisor callbacks %% =================================================================== init([]) -> {ok, { {one_for_one, 5, 10}, []} }. rebar-2.0.0/priv/templates/simplefsm.erl000066400000000000000000000033171176314626300203020ustar00rootroot00000000000000-module({{fsmid}}). -behaviour(gen_fsm). -define(SERVER, ?MODULE). %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([start_link/0]). %% ------------------------------------------------------------------ %% gen_fsm Function Exports %% ------------------------------------------------------------------ -export([init/1, state_name/2, state_name/3, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ start_link() -> gen_fsm:start_link({local, ?SERVER}, ?MODULE, [], []). %% ------------------------------------------------------------------ %% gen_fsm Function Definitions %% ------------------------------------------------------------------ init(_Args) -> {ok, initial_state_name, initial_state}. state_name(_Event, State) -> {next_state, state_name, State}. state_name(_Event, _From, State) -> {reply, ok, state_name, State}. handle_event(_Event, StateName, State) -> {next_state, StateName, State}. handle_sync_event(_Event, _From, StateName, State) -> {reply, ok, StateName, State}. handle_info(_Info, StateName, State) -> {next_state, StateName, State}. terminate(_Reason, _StateName, _State) -> ok. code_change(_OldVsn, StateName, State, _Extra) -> {ok, StateName, State}. %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ rebar-2.0.0/priv/templates/simplefsm.template000066400000000000000000000001231176314626300213230ustar00rootroot00000000000000{variables, [{fsmid, "myfsm"}]}. {template, "simplefsm.erl", "src/{{fsmid}}.erl"}. rebar-2.0.0/priv/templates/simplemod.erl000066400000000000000000000001011176314626300202600ustar00rootroot00000000000000-module({{modid}}). -export([my_func/0]). my_func() -> ok. rebar-2.0.0/priv/templates/simplemod.template000066400000000000000000000002221176314626300213150ustar00rootroot00000000000000{variables, [{modid, "mymod"}]}. {template, "simplemod.erl", "src/{{modid}}.erl"}. {template, "simplemod_tests.erl", "test/{{modid}}_tests.erl"}. rebar-2.0.0/priv/templates/simplemod_tests.erl000066400000000000000000000001041176314626300215050ustar00rootroot00000000000000-module({{modid}}_tests). -include_lib("eunit/include/eunit.hrl"). rebar-2.0.0/priv/templates/simplenode.erl.script000066400000000000000000000021361176314626300217430ustar00rootroot00000000000000#!/bin/sh ## This script replaces the default "erl" in erts-VSN/bin. This is necessary ## as escript depends on erl and in turn, erl depends on having access to a ## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect ## of running escript -- the embedded node bypasses erl and uses erlexec directly ## (as it should). ## ## Note that this script makes the assumption that there is a start_clean.boot ## file available in $ROOTDIR/release/VSN. # Determine the abspath of where this script is executing from. ERTS_BIN_DIR=$(cd ${0%/*} && pwd) # Now determine the root directory -- this script runs from erts-VSN/bin, # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR # path. ROOTDIR=${ERTS_BIN_DIR%/*/*} # Parse out release and erts info START_ERL=`cat $ROOTDIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` CMD="$BINDIR/erlexec" export EMU export ROOTDIR export BINDIR export PROGNAME exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"} rebar-2.0.0/priv/templates/simplenode.install_upgrade.escript000066400000000000000000000032561176314626300245070ustar00rootroot00000000000000#!/usr/bin/env escript %%! -noshell -noinput %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et -define(TIMEOUT, 60000). -define(INFO(Fmt,Args), io:format(Fmt,Args)). main([NodeName, Cookie, ReleasePackage]) -> TargetNode = start_distribution(NodeName, Cookie), {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release, [ReleasePackage], ?TIMEOUT), ?INFO("Unpacked Release ~p~n", [Vsn]), {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, check_install_release, [Vsn], ?TIMEOUT), {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, install_release, [Vsn], ?TIMEOUT), ?INFO("Installed Release ~p~n", [Vsn]), ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT), ?INFO("Made Release ~p Permanent~n", [Vsn]); main(_) -> init:stop(1). start_distribution(NodeName, Cookie) -> MyNode = make_script_node(NodeName), {ok, _Pid} = net_kernel:start([MyNode, shortnames]), erlang:set_cookie(node(), list_to_atom(Cookie)), TargetNode = make_target_node(NodeName), case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of {true, pong} -> ok; {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), init:stop(1) end, TargetNode. make_target_node(Node) -> [_, Host] = string:tokens(atom_to_list(node()), "@"), list_to_atom(lists:concat([Node, "@", Host])). make_script_node(Node) -> list_to_atom(lists:concat([Node, "_upgrader_", os:getpid()])). rebar-2.0.0/priv/templates/simplenode.nodetool000066400000000000000000000113231176314626300214770ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% nodetool: Helper Script for interacting with live nodes %% %% ------------------------------------------------------------------- main(Args) -> ok = start_epmd(), %% Extract the args {RestArgs, TargetNode} = process_args(Args, [], undefined), %% See if the node is currently running -- if it's not, we'll bail case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of {true, pong} -> ok; {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of ["ping"] -> %% If we got this far, the node already responsed to a ping, so just dump %% a "pong" io:format("pong\n"); ["stop"] -> io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]); ["restart"] -> io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]); ["reboot"] -> io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); ["rpc", Module, Function | RpcArgs] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), [RpcArgs], 60000) of ok -> ok; {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); _ -> halt(1) end; ["rpcterms", Module, Function, ArgsAsString] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), consult(ArgsAsString), 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); Other -> io:format("~p\n", [Other]) end; Other -> io:format("Other: ~p\n", [Other]), io:format("Usage: nodetool {ping|stop|restart|reboot}\n") end, net_kernel:stop(). process_args([], Acc, TargetNode) -> {lists:reverse(Acc), TargetNode}; process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) -> erlang:set_cookie(node(), list_to_atom(Cookie)), process_args(Rest, Acc, TargetNode); process_args(["-name", TargetName | Rest], Acc, _) -> ThisNode = append_node_suffix(TargetName, "_maint_"), {ok, _} = net_kernel:start([ThisNode, longnames]), process_args(Rest, Acc, nodename(TargetName)); process_args(["-sname", TargetName | Rest], Acc, _) -> ThisNode = append_node_suffix(TargetName, "_maint_"), {ok, _} = net_kernel:start([ThisNode, shortnames]), process_args(Rest, Acc, nodename(TargetName)); process_args([Arg | Rest], Acc, Opts) -> process_args(Rest, [Arg | Acc], Opts). start_epmd() -> [] = os:cmd(epmd_path() ++ " -daemon"), ok. epmd_path() -> ErtsBinDir = filename:dirname(escript:script_name()), Name = "epmd", case os:find_executable(Name, ErtsBinDir) of false -> case os:find_executable(Name) of false -> io:format("Could not find epmd.~n"), halt(1); GlobalEpmd -> GlobalEpmd end; Epmd -> Epmd end. nodename(Name) -> case string:tokens(Name, "@") of [_Node, _Host] -> list_to_atom(Name); [Node] -> [_, Host] = string:tokens(atom_to_list(node()), "@"), list_to_atom(lists:concat([Node, "@", Host])) end. append_node_suffix(Name, Suffix) -> case string:tokens(Name, "@") of [Node, Host] -> list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host])); [Node] -> list_to_atom(lists:concat([Node, Suffix, os:getpid()])) end. %% %% Given a string or binary, parse it into a list of terms, ala file:consult/0 %% consult(Str) when is_list(Str) -> consult([], Str, []); consult(Bin) when is_binary(Bin)-> consult([], binary_to_list(Bin), []). consult(Cont, Str, Acc) -> case erl_scan:tokens(Cont, Str, 0) of {done, Result, Remaining} -> case Result of {ok, Tokens, _} -> {ok, Term} = erl_parse:parse_term(Tokens), consult([], Remaining, [Term | Acc]); {eof, _Other} -> lists:reverse(Acc); {error, Info, _} -> {error, Info} end; {more, Cont1} -> consult(Cont1, eof, Acc) end. rebar-2.0.0/priv/templates/simplenode.reltool.config000066400000000000000000000027331176314626300226050ustar00rootroot00000000000000{sys, [ {lib_dirs, []}, {erts, [{mod_cond, derived}, {app_file, strip}]}, {app_file, strip}, {rel, "{{nodeid}}", "1", [ kernel, stdlib, sasl, {{nodeid}} ]}, {rel, "start_clean", "", [ kernel, stdlib ]}, {boot_rel, "{{nodeid}}"}, {profile, embedded}, {incl_cond, exclude}, {excl_archive_filters, [".*"]}, %% Do not archive built libs {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)", "^erts.*/(doc|info|include|lib|man|src)"]}, {excl_app_filters, ["\.gitignore"]}, {app, sasl, [{incl_cond, include}]}, {app, stdlib, [{incl_cond, include}]}, {app, kernel, [{incl_cond, include}]}, {app, {{nodeid}}, [{incl_cond, include}]} ]}. {target_dir, "{{nodeid}}"}. {overlay, [ {mkdir, "log/sasl"}, {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"}, {copy, "files/{{nodeid}}", "bin/{{nodeid}}"}, {copy, "files/{{nodeid}}.cmd", "bin/{{nodeid}}.cmd"}, {copy, "files/start_erl.cmd", "bin/start_erl.cmd"}, {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"}, {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"}, {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"} ]}. rebar-2.0.0/priv/templates/simplenode.runner000077500000000000000000000164331176314626300211770ustar00rootroot00000000000000#!/bin/sh # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd) RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*} RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log # Note the trailing slash on $PIPE_DIR/ PIPE_DIR=/tmp/$RUNNER_BASE_DIR/ RUNNER_USER= # Make sure this script is running as the appropriate user if [ ! -z "$RUNNER_USER" ] && [ `whoami` != "$RUNNER_USER" ]; then exec sudo -u $RUNNER_USER -i $0 $@ fi # Make sure CWD is set to runner base dir cd $RUNNER_BASE_DIR # Make sure log directory exists mkdir -p $RUNNER_LOG_DIR # Identify the script name SCRIPT=`basename $0` # Parse out release and erts info START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } # Use releases/VSN/vm.args if it exists otherwise use etc/vm.args if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" ]; then VMARGS_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" else VMARGS_PATH="$RUNNER_ETC_DIR/vm.args" fi # Use releases/VSN/sys.config if it exists otherwise use etc/app.config if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" ]; then CONFIG_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" else CONFIG_PATH="$RUNNER_ETC_DIR/app.config" fi # Extract the target node name from node.args NAME_ARG=`egrep '^-s?name' $VMARGS_PATH` if [ -z "$NAME_ARG" ]; then echo "vm.args needs to have either -name or -sname parameter." exit 1 fi # Extract the name type and name from the NAME_ARG for REMSH REMSH_TYPE=`echo $NAME_ARG | awk '{print $1}'` REMSH_NAME=`echo $NAME_ARG | awk '{print $2}'` # Note the `date +%s`, used to allow multiple remsh to the same node transparently REMSH_NAME_ARG="$REMSH_TYPE remsh`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`" REMSH_REMSH_ARG="-remsh $REMSH_NAME" # Extract the target cookie COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH` if [ -z "$COOKIE_ARG" ]; then echo "vm.args needs to have a -setcookie parameter." exit 1 fi # Add ERTS bin dir to our path ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin # Setup command to control the node NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" # Setup remote shell command to control node REMSH="$ERTS_PATH/erl $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG" # Check the first argument for instructions case "$1" in start) # Make sure there is not already a node running RES=`$NODETOOL ping` if [ "$RES" = "pong" ]; then echo "Node is already running!" exit 1 fi shift # remove $1 RUN_PARAM=$(printf "\'%s\' " "$@") HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start $RUN_PARAM" export HEART_COMMAND mkdir -p $PIPE_DIR $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $RUN_PARAM" 2>&1 ;; stop) # Wait for the node to completely stop... case `uname -s` in Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD) # PID COMMAND PID=`ps ax -o pid= -o command=|\ grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` ;; SunOS) # PID COMMAND PID=`ps -ef -o pid= -o args=|\ grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` ;; CYGWIN*) # UID PID PPID TTY STIME COMMAND PID=`ps -efW|grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $2}'` ;; esac $NODETOOL stop ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi while `kill -0 $PID 2>/dev/null`; do sleep 1 done ;; restart) ## Restart the VM without exiting the process $NODETOOL restart ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; reboot) ## Restart the VM completely (uses heart to restart it) $NODETOOL reboot ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; ping) ## See if the VM is alive $NODETOOL ping ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; attach) # Make sure a node IS running RES=`$NODETOOL ping` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift exec $ERTS_PATH/to_erl $PIPE_DIR ;; remote_console) # Make sure a node IS running RES=`$NODETOOL ping` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift exec $REMSH ;; upgrade) if [ -z "$2" ]; then echo "Missing upgrade package argument" echo "Usage: $SCRIPT upgrade {package base name}" echo "NOTE {package base name} MUST NOT include the .tar.gz suffix" exit 1 fi # Make sure a node IS running RES=`$NODETOOL ping` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi node_name=`echo $NAME_ARG | awk '{print $2}'` erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'` $ERTS_PATH/escript $RUNNER_BASE_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2 ;; console|console_clean) # .boot file typically just $SCRIPT (ie, the app name) # however, for debugging, sometimes start_clean.boot is useful: case "$1" in console) BOOTFILE=$SCRIPT ;; console_clean) BOOTFILE=start_clean ;; esac # Setup beam-required vars ROOTDIR=$RUNNER_BASE_DIR BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR export PROGNAME # Dump environment info for logging purposes echo "Exec: $CMD" -- ${1+"$@"} echo "Root: $ROOTDIR" # Log the startup logger -t "$SCRIPT[$$]" "Starting up" # Start the VM exec $CMD -- ${1+"$@"} ;; foreground) # start up the release in the foreground for use by runit # or other supervision services BOOTFILE=$SCRIPT FOREGROUNDOPTIONS="-noinput +Bd" # Setup beam-required vars ROOTDIR=$RUNNER_BASE_DIR BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\///'` CMD="$BINDIR/erlexec $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR export PROGNAME # Dump environment info for logging purposes echo "Exec: $CMD" -- ${1+"$@"} echo "Root: $ROOTDIR" # Start the VM exec $CMD -- ${1+"$@"} ;; *) echo "Usage: $SCRIPT {start|foreground|stop|restart|reboot|ping|console|console_clean|attach|remote_console|upgrade}" exit 1 ;; esac exit 0 rebar-2.0.0/priv/templates/simplenode.sys.config000066400000000000000000000005151176314626300217370ustar00rootroot00000000000000[ %% SASL config {sasl, [ {sasl_error_logger, {file, "log/sasl-error.log"}}, {errlog_type, error}, {error_logger_mf_dir, "log/sasl"}, % Log directory {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size {error_logger_mf_maxfiles, 5} % 5 files max ]} ]. rebar-2.0.0/priv/templates/simplenode.template000066400000000000000000000012201176314626300214620ustar00rootroot00000000000000{variables, [{nodeid, "mynode"}]}. {template, "simplenode.reltool.config", "reltool.config"}. {file, "simplenode.erl.script", "files/erl"}. {chmod, 8#744, "files/erl"}. {file, "simplenode.nodetool", "files/nodetool"}. {chmod, 8#744, "files/nodetool"}. {file, "simplenode.runner", "files/{{nodeid}}"}. {chmod, 8#744, "files/{{nodeid}}"}. {file, "simplenode.sys.config", "files/sys.config"}. {template, "simplenode.vm.args", "files/vm.args"}. {template, "simplenode.windows.runner.cmd", "files/{{nodeid}}.cmd"}. {file, "simplenode.windows.start_erl.cmd", "files/start_erl.cmd"}. {file, "simplenode.install_upgrade.escript", "files/install_upgrade.escript"}. rebar-2.0.0/priv/templates/simplenode.vm.args000066400000000000000000000006611176314626300212340ustar00rootroot00000000000000## Name of the node -name {{nodeid}}@127.0.0.1 ## Cookie for distributed erlang -setcookie {{nodeid}} ## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive ## (Disabled by default..use with caution!) ##-heart ## Enable kernel poll and a few async threads ##+K true ##+A 5 ## Increase number of concurrent ports/sockets ##-env ERL_MAX_PORTS 4096 ## Tweak GC to run more often ##-env ERL_FULLSWEEP_AFTER 10 rebar-2.0.0/priv/templates/simplenode.windows.runner.cmd000066400000000000000000000047361176314626300234320ustar00rootroot00000000000000@setlocal @set node_name={{nodeid}} @rem Get the absolute path to the parent directory, @rem which is assumed to be the node root. @for /F "delims=" %%I in ("%~dp0..") do @set node_root=%%~fI @set releases_dir=%node_root%\releases @rem Parse ERTS version and release version from start_erl.data @for /F "tokens=1,2" %%I in (%releases_dir%\start_erl.data) do @( @call :set_trim erts_version %%I @call :set_trim release_version %%J ) @rem extract erlang cookie from vm.args @set vm_args=%releases_dir%\%release_version%\vm.args @for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie %vm_args%`) do @set erlang_cookie=%%J @set erts_bin=%node_root%\erts-%erts_version%\bin @set service_name=%node_name%_%release_version% @if "%1"=="usage" @goto usage @if "%1"=="install" @goto install @if "%1"=="uninstall" @goto uninstall @if "%1"=="start" @goto start @if "%1"=="stop" @goto stop @if "%1"=="restart" @call :stop && @goto start @if "%1"=="console" @goto console @if "%1"=="query" @goto query @if "%1"=="attach" @goto attach @if "%1"=="upgrade" @goto upgrade @echo Unknown command: "%1" :usage @echo Usage: %~n0 [install^|uninstall^|start^|stop^|restart^|console^|query^|attach^|upgrade] @goto :EOF :install @%erts_bin%\erlsrv.exe add %service_name% -c "Erlang node %node_name% in %node_root%" -sname %node_name% -w %node_root% -m %node_root%\bin\start_erl.cmd -args " ++ %node_name% ++ %node_root%" -stopaction "init:stop()." @goto :EOF :uninstall @%erts_bin%\erlsrv.exe remove %service_name% @%erts_bin%\epmd.exe -kill @goto :EOF :start @%erts_bin%\erlsrv.exe start %service_name% @goto :EOF :stop @%erts_bin%\erlsrv.exe stop %service_name% @goto :EOF :console @start %erts_bin%\werl.exe -boot %releases_dir%\%release_version%\%node_name% -config %releases_dir%\%release_version%\sys.config -args_file %vm_args% -sname %node_name% @goto :EOF :query @%erts_bin%\erlsrv.exe list %service_name% @exit /b %ERRORLEVEL% @goto :EOF :attach @for /f "usebackq" %%I in (`hostname`) do @set hostname=%%I start %erts_bin%\werl.exe -boot %releases_dir%\%release_version%\start_clean -remsh %node_name%@%hostname% -sname console -setcookie %erlang_cookie% @goto :EOF :upgrade @if "%2"=="" ( @echo Missing upgrade package argument @echo Usage: %~n0 upgrade {package base name} @echo NOTE {package base name} MUST NOT include the .tar.gz suffix @goto :EOF ) @%erts_bin%\escript.exe %node_root%\bin\install_upgrade.escript %node_name% %erlang_cookie% %2 @goto :EOF :set_trim @set %1=%2 @goto :EOF rebar-2.0.0/priv/templates/simplenode.windows.start_erl.cmd000066400000000000000000000021321176314626300241040ustar00rootroot00000000000000@setlocal @rem Parse arguments. erlsrv.exe prepends erl arguments prior to first ++. @rem Other args are position dependent. @set args="%*" @for /F "delims=++ tokens=1,2,3" %%I in (%args%) do @( @set erl_args=%%I @call :set_trim node_name %%J @call :set_trim node_root %%K ) @set releases_dir=%node_root%\releases @rem parse ERTS version and release version from start_erl.dat @for /F "tokens=1,2" %%I in (%releases_dir%\start_erl.data) do @( @call :set_trim erts_version %%I @call :set_trim release_version %%J ) @set erl_exe=%node_root%\erts-%erts_version%\bin\erl.exe @set boot_file=%releases_dir%\%release_version%\%node_name% @if exist %releases_dir%\%release_version%\sys.config ( @set app_config=%releases_dir%\%release_version%\sys.config ) else ( @set app_config=%node_root%\etc\app.config ) @if exist %releases_dir%\%release_version%\vm.args ( @set vm_args=%releases_dir%\%release_version%\vm.args ) else ( @set vm_args=%node_root%\etc\vm.args ) @%erl_exe% %erl_args% -boot %boot_file% -config %app_config% -args_file %vm_args% :set_trim @set %1=%2 @goto :EOF rebar-2.0.0/priv/templates/simplesrv.erl000066400000000000000000000026241176314626300203270ustar00rootroot00000000000000-module({{srvid}}). -behaviour(gen_server). -define(SERVER, ?MODULE). %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([start_link/0]). %% ------------------------------------------------------------------ %% gen_server Function Exports %% ------------------------------------------------------------------ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %% ------------------------------------------------------------------ %% gen_server Function Definitions %% ------------------------------------------------------------------ init(Args) -> {ok, Args}. handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ rebar-2.0.0/priv/templates/simplesrv.template000066400000000000000000000001261176314626300213530ustar00rootroot00000000000000{variables, [{srvid, "myserver"}]}. {template, "simplesrv.erl", "src/{{srvid}}.erl"}. rebar-2.0.0/rebar.config000066400000000000000000000007441176314626300151040ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {app_bin, ["priv/rebar"]}. {erl_opts, [warnings_as_errors]}. {xref_checks, []}. {xref_queries, [{"(XC - UC) || (XU - X - B - (\"escript\":\"foldl\"/\"3\") - (\"abnfc\":\"file\"/\"2\") - (\"erlydtl\":\"compile\"/\"3\") - (\"lfe_comp\":\"file\"/\"2\") - (\"neotoma\":\"file\"/\"2\") - (\"protobuffs_compile\":\"scan_file\"/\"1\"))", []}]}. rebar-2.0.0/rebar.config.sample000066400000000000000000000114101176314626300163540ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et %% This is a sample rebar.conf file that shows examples of some of rebar's %% options. %% == Core == %% Additional library directories to add to the code path {lib_dirs, []}. %% == Erlang Compiler == %% Erlang files to compile before the rest. Rebar automatically compiles %% parse_transforms and custom behaviours before anything other than the files %% in this list. {erl_first_files, ["mymib1", "mymib2"]}. %% Erlang compiler options {erl_opts, [no_debug_info, {i, "myinclude"}, {src_dirs, ["src1", "src2"]}, {platform_define, "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'}, {platform_define, "(linux|freebsd)", 'BACKLOG', 128}, {platform_define, "R13", 'old_inets'}]}. %% MIB Options? {mib_opts, []}. %% SNMP mibs to compile first? {mib_first_files, []}. %% == EDoc == %% EDoc options {edoc_opts, []}. %% == Port Compiler == %% Port compilation environment variables. See rebar_port_compiler.erl for %% more info. Default is `[]' {port_env, [{"CFLAGS", "$CFLAGS -Ifoo"}, {"freebsd", "LDFLAGS", "$LDFLAGS -lfoo"}]}. %% port_specs %% List of filenames or wildcards to be compiled. May also contain a tuple %% consisting of a regular expression to be applied against the system %% architecture as a filter. {port_specs, [{"priv/so_name.so", ["c_src/*.c"]}, {"linux", "priv/hello_linux", ["c_src/hello_linux.c"]}, {"linux", "priv/hello_linux", ["c_src/*.c"], [{env, []}]}}. %% == LFE Compiler == %% LFE files to compile before the rest {lfe_first_files, []}. %% Options for the LFE compiler: reuse {erl_opts, []} %% == ErlyDTL Compiler == %% Options for the ErlyDTL compiler {erlydtl_opts, []}. %% == EUnit == %% Options for eunit:test() {eunit_opts, []}. %% Additional compile options for eunit. erl_opts from above is also used {eunit_compile_opts, []}. %% Same as erl_first_files, but used only when running 'eunit' {eunit_first_files, []}. %% Whether to enable coverage reporting. Default is `false' {cover_enabled, false}. %% Whether to print coverage report to console. Default is `false' {cover_print_enabled, false}. %% == Common Test == %% Override the default "test" directory in which SUITEs are located {ct_dir, "itest"}. %% Option to pass extra parameters when launching Common Test {ct_extra_params, "-boot start_sasl -s myapp"}. %% == Cleanup == %% Which files to cleanup {clean_files, ["file", "file2"]}. %% == Reltool == %% Target directory for the release {target, "target"}. %% == OTP Applications == %% Binaries to link into the erlang path? {app_bin, []}. %% Enable validation of the OTP app module list. Default is 'true' {validate_app_modules, true}. %% == Dependencies == %% Where to put any downloaded dependencies. Default is "deps" {deps_dir, "deps"}. %% What dependencies we have, dependencies can be of 3 forms, an application %% name as an atom, eg. mochiweb, a name and a version (from the .app file), or %% an application name, a version and the SCM details on how to fetch it (SCM %% type, location and revision). Rebar currently supports git, hg, bzr and svn. {deps, [application_name, {application_name, "1.0.*"}, {application_name, "1.0.*", {git, "git://github.com/basho/rebar.git", {branch, "master"}}}]}. %% == Subdirectories == %% Subdirectories? {sub_dirs, ["dir1", "dir2"]}. %% == Plugins == %% Plugins you wish to include. %% These can include any module on the code path, including deps. %% Alternatively, plugins can be placed as source files in the plugin_dir, in %% which case they will be compiled and loaded dynamically at runtime. {plugins, [plugin1, plugin2]}. %% Override the directory in which plugin sources can be found. %% Defaults to ./plugins {plugin_dir, "some_other_directory"}. %% == Pre/Post Command Hooks == {pre_hooks, [{clean, "./prepare_package_files.sh"}, {"linux", compile, "c_src/build_linux.sh"}, {compile, "escript generate_headers"}, {compile, "escript check_headers"}]}. {post_hooks, [{clean, "touch file1.out"}, {"freebsd", compile, "c_src/freebsd_tweaks.sh"}, {eunit, "touch file2.out"}, {compile, "touch postcompile.out"}]}. %% == xref == {xref_warnings, false}. %% xref checks to run {xref_checks, [exports_not_used, undefined_function_calls]}. %% Optional custom xref queries (xref manual has details) specified as %% {xref_queries, [{query_string(), expected_query_result()},...]} %% The following for example removes all references to ejabberd:*_msg/4 %% functions from undefined external function calls as those are in a %% generated module {xref_queries, [{"(XC - UC) || (XU - X - B" " - (\"ejabberd_logger\":\".*_msg\"/\"4\"))",[]}]}. rebar-2.0.0/src/000077500000000000000000000000001176314626300134045ustar00rootroot00000000000000rebar-2.0.0/src/getopt.erl000066400000000000000000000631611176314626300154210ustar00rootroot00000000000000%%%------------------------------------------------------------------- %%% @author Juan Jose Comellas %%% @copyright (C) 2009 Juan Jose Comellas %%% @doc Parses command line options with a format similar to that of GNU getopt. %%% @end %%% %%% This source file is subject to the New BSD License. You should have received %%% a copy of the New BSD license with this software. If not, it can be %%% retrieved from: http://www.opensource.org/licenses/bsd-license.php %%%------------------------------------------------------------------- -module(getopt). -author('juanjo@comellas.org'). -export([parse/2, usage/2, usage/3, usage/4]). -export_type([arg_type/0, arg_value/0, arg_spec/0, simple_option/0, compound_option/0, option/0, option_spec/0]). -define(TAB_LENGTH, 8). %% Indentation of the help messages in number of tabs. -define(INDENTATION, 3). %% Position of each field in the option specification tuple. -define(OPT_NAME, 1). -define(OPT_SHORT, 2). -define(OPT_LONG, 3). -define(OPT_ARG, 4). -define(OPT_HELP, 5). -define(IS_OPT_SPEC(Opt), (tuple_size(Opt) =:= ?OPT_HELP)). %% Atom indicating the data type that an argument can be converted to. -type arg_type() :: 'atom' | 'binary' | 'boolean' | 'float' | 'integer' | 'string'. %% Data type that an argument can be converted to. -type arg_value() :: atom() | binary() | boolean() | float() | integer() | string(). %% Argument specification. -type arg_spec() :: arg_type() | {arg_type(), arg_value()} | undefined. %% Option type and optional default argument. -type simple_option() :: atom(). -type compound_option() :: {atom(), arg_value()}. -type option() :: simple_option() | compound_option(). %% Command line option specification. -type option_spec() :: { Name :: atom(), Short :: char() | undefined, Long :: string() | undefined, ArgSpec :: arg_spec(), Help :: string() | undefined }. %% Output streams -type output_stream() :: 'standard_io' | 'standard_error'. %% @doc Parse the command line options and arguments returning a list of tuples %% and/or atoms using the Erlang convention for sending options to a %% function. -spec parse([option_spec()], string() | [string()]) -> {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: any()}}. parse(OptSpecList, CmdLine) -> try Args = if is_integer(hd(CmdLine)) -> string:tokens(CmdLine, " \t\n"); true -> CmdLine end, parse(OptSpecList, [], [], 0, Args) catch throw: {error, {_Reason, _Data}} = Error -> Error end. -spec parse([option_spec()], [option()], [string()], integer(), [string()]) -> {ok, {[option()], [string()]}}. %% Process the option terminator. parse(OptSpecList, OptAcc, ArgAcc, _ArgPos, ["--" | Tail]) -> %% Any argument present after the terminator is not considered an option. {ok, {lists:reverse(append_default_options(OptSpecList, OptAcc)), lists:reverse(ArgAcc, Tail)}}; %% Process long options. parse(OptSpecList, OptAcc, ArgAcc, ArgPos, ["--" ++ OptArg = OptStr | Tail]) -> parse_long_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Tail, OptStr, OptArg); %% Process short options. parse(OptSpecList, OptAcc, ArgAcc, ArgPos, ["-" ++ ([_Char | _] = OptArg) = OptStr | Tail]) -> parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Tail, OptStr, OptArg); %% Process non-option arguments. parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [Arg | Tail]) -> case find_non_option_arg(OptSpecList, ArgPos) of {value, OptSpec} when ?IS_OPT_SPEC(OptSpec) -> parse(OptSpecList, add_option_with_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos + 1, Tail); false -> parse(OptSpecList, OptAcc, [Arg | ArgAcc], ArgPos, Tail) end; parse(OptSpecList, OptAcc, ArgAcc, _ArgPos, []) -> %% Once we have completed gathering the options we add the ones that were %% not present but had default arguments in the specification. {ok, {lists:reverse(append_default_options(OptSpecList, OptAcc)), lists:reverse(ArgAcc)}}. %% @doc Parse a long option, add it to the option accumulator and continue %% parsing the rest of the arguments recursively. %% A long option can have the following syntax: %% --foo Single option 'foo', no argument %% --foo=bar Single option 'foo', argument "bar" %% --foo bar Single option 'foo', argument "bar" -spec parse_long_option([option_spec()], [option()], [string()], integer(), [string()], string(), string()) -> {ok, {[option()], [string()]}}. parse_long_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) -> case split_assigned_arg(OptArg) of {Long, Arg} -> %% Get option that has its argument within the same string %% separated by an equal ('=') character (e.g. "--port=1000"). parse_long_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Long, Arg); Long -> case lists:keyfind(Long, ?OPT_LONG, OptSpecList) of {Name, _Short, Long, undefined, _Help} -> parse(OptSpecList, [Name | OptAcc], ArgAcc, ArgPos, Args); {_Name, _Short, Long, _ArgSpec, _Help} = OptSpec -> %% The option argument string is empty, but the option requires %% an argument, so we look into the next string in the list. %% e.g ["--port", "1000"] parse_long_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec); false -> throw({error, {invalid_option, OptStr}}) end end. %% @doc Parse an option where the argument is 'assigned' in the same string using %% the '=' character, add it to the option accumulator and continue parsing the %% rest of the arguments recursively. This syntax is only valid for long options. -spec parse_long_option_assigned_arg([option_spec()], [option()], [string()], integer(), [string()], string(), string(), string()) -> {ok, {[option()], [string()]}}. parse_long_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Long, Arg) -> case lists:keyfind(Long, ?OPT_LONG, OptSpecList) of {_Name, _Short, Long, ArgSpec, _Help} = OptSpec -> case ArgSpec of undefined -> throw({error, {invalid_option_arg, OptStr}}); _ -> parse(OptSpecList, add_option_with_assigned_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos, Args) end; false -> throw({error, {invalid_option, OptStr}}) end. %% @doc Split an option string that may contain an option with its argument %% separated by an equal ('=') character (e.g. "port=1000"). -spec split_assigned_arg(string()) -> {Name :: string(), Arg :: string()} | string(). split_assigned_arg(OptStr) -> split_assigned_arg(OptStr, OptStr, []). split_assigned_arg(_OptStr, "=" ++ Tail, Acc) -> {lists:reverse(Acc), Tail}; split_assigned_arg(OptStr, [Char | Tail], Acc) -> split_assigned_arg(OptStr, Tail, [Char | Acc]); split_assigned_arg(OptStr, [], _Acc) -> OptStr. %% @doc Retrieve the argument for an option from the next string in the list of %% command-line parameters or set the value of the argument from the argument %% specification (for boolean and integer arguments), if possible. parse_long_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, {Name, _Short, _Long, ArgSpec, _Help} = OptSpec) -> ArgSpecType = arg_spec_type(ArgSpec), case Args =:= [] orelse is_implicit_arg(ArgSpecType, hd(Args)) of true -> parse(OptSpecList, add_option_with_implicit_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args); false -> [Arg | Tail] = Args, try parse(OptSpecList, [{Name, to_type(ArgSpecType, Arg)} | OptAcc], ArgAcc, ArgPos, Tail) catch error:_ -> throw({error, {invalid_option_arg, {Name, Arg}}}) end end. %% @doc Parse a short option, add it to the option accumulator and continue %% parsing the rest of the arguments recursively. %% A short option can have the following syntax: %% -a Single option 'a', no argument or implicit boolean argument %% -a foo Single option 'a', argument "foo" %% -afoo Single option 'a', argument "foo" %% -abc Multiple options: 'a'; 'b'; 'c' %% -bcafoo Multiple options: 'b'; 'c'; 'a' with argument "foo" %% -aaa Multiple repetitions of option 'a' (only valid for options with integer arguments) -spec parse_short_option([option_spec()], [option()], [string()], integer(), [string()], string(), string()) -> {ok, {[option()], [string()]}}. parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) -> parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, first, OptArg). parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptPos, [Short | Arg]) -> case lists:keyfind(Short, ?OPT_SHORT, OptSpecList) of {Name, Short, _Long, undefined, _Help} -> parse_short_option(OptSpecList, [Name | OptAcc], ArgAcc, ArgPos, Args, OptStr, first, Arg); {_Name, Short, _Long, ArgSpec, _Help} = OptSpec -> %% The option has a specification, so it requires an argument. case Arg of [] -> %% The option argument string is empty, but the option requires %% an argument, so we look into the next string in the list. parse_short_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec, OptPos); _ -> case is_valid_arg(ArgSpec, Arg) of true -> parse(OptSpecList, add_option_with_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos, Args); _ -> NewOptAcc = case OptPos of first -> add_option_with_implicit_arg(OptSpec, OptAcc); _ -> add_option_with_implicit_incrementable_arg(OptSpec, OptAcc) end, parse_short_option(OptSpecList, NewOptAcc, ArgAcc, ArgPos, Args, OptStr, next, Arg) end end; false -> throw({error, {invalid_option, OptStr}}) end; parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, _OptStr, _OptPos, []) -> parse(OptSpecList, OptAcc, ArgAcc, ArgPos, Args). %% @doc Retrieve the argument for an option from the next string in the list of %% command-line parameters or set the value of the argument from the argument %% specification (for boolean and integer arguments), if possible. parse_short_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, {Name, _Short, _Long, ArgSpec, _Help} = OptSpec, OptPos) -> case Args =:= [] orelse is_implicit_arg(ArgSpec, hd(Args)) of true when OptPos =:= first -> parse(OptSpecList, add_option_with_implicit_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args); true -> parse(OptSpecList, add_option_with_implicit_incrementable_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args); false -> [Arg | Tail] = Args, try parse(OptSpecList, [{Name, to_type(ArgSpec, Arg)} | OptAcc], ArgAcc, ArgPos, Tail) catch error:_ -> throw({error, {invalid_option_arg, {Name, Arg}}}) end end. %% @doc Find the option for the discrete argument in position specified in the %% Pos argument. -spec find_non_option_arg([option_spec()], integer()) -> {value, option_spec()} | false. find_non_option_arg([{_Name, undefined, undefined, _ArgSpec, _Help} = OptSpec | _Tail], 0) -> {value, OptSpec}; find_non_option_arg([{_Name, undefined, undefined, _ArgSpec, _Help} | Tail], Pos) -> find_non_option_arg(Tail, Pos - 1); find_non_option_arg([_Head | Tail], Pos) -> find_non_option_arg(Tail, Pos); find_non_option_arg([], _Pos) -> false. %% @doc Append options that were not present in the command line arguments with %% their default arguments. -spec append_default_options([option_spec()], [option()]) -> [option()]. append_default_options([{Name, _Short, _Long, {_Type, DefaultArg}, _Help} | Tail], OptAcc) -> append_default_options(Tail, case lists:keymember(Name, 1, OptAcc) of false -> [{Name, DefaultArg} | OptAcc]; _ -> OptAcc end); %% For options with no default argument. append_default_options([_Head | Tail], OptAcc) -> append_default_options(Tail, OptAcc); append_default_options([], OptAcc) -> OptAcc. %% @doc Add an option with argument converting it to the data type indicated by the %% argument specification. -spec add_option_with_arg(option_spec(), string(), [option()]) -> [option()]. add_option_with_arg({Name, _Short, _Long, ArgSpec, _Help} = OptSpec, Arg, OptAcc) -> case is_valid_arg(ArgSpec, Arg) of true -> try [{Name, to_type(ArgSpec, Arg)} | OptAcc] catch error:_ -> throw({error, {invalid_option_arg, {Name, Arg}}}) end; false -> add_option_with_implicit_arg(OptSpec, OptAcc) end. %% @doc Add an option with argument that was part of an assignment expression %% (e.g. "--verbose=3") converting it to the data type indicated by the %% argument specification. -spec add_option_with_assigned_arg(option_spec(), string(), [option()]) -> [option()]. add_option_with_assigned_arg({Name, _Short, _Long, ArgSpec, _Help}, Arg, OptAcc) -> try [{Name, to_type(ArgSpec, Arg)} | OptAcc] catch error:_ -> throw({error, {invalid_option_arg, {Name, Arg}}}) end. %% @doc Add an option that required an argument but did not have one. Some data %% types (boolean, integer) allow implicit or assumed arguments. -spec add_option_with_implicit_arg(option_spec(), [option()]) -> [option()]. add_option_with_implicit_arg({Name, _Short, _Long, ArgSpec, _Help}, OptAcc) -> case arg_spec_type(ArgSpec) of boolean -> %% Special case for boolean arguments: if there is no argument we %% set the value to 'true'. [{Name, true} | OptAcc]; integer -> %% Special case for integer arguments: if the option had not been set %% before we set the value to 1. This is needed to support options like %% "-v" to return something like {verbose, 1}. [{Name, 1} | OptAcc]; _ -> throw({error, {missing_option_arg, Name}}) end. %% @doc Add an option with an implicit or assumed argument. -spec add_option_with_implicit_incrementable_arg(option_spec() | arg_spec(), [option()]) -> [option()]. add_option_with_implicit_incrementable_arg({Name, _Short, _Long, ArgSpec, _Help}, OptAcc) -> case arg_spec_type(ArgSpec) of boolean -> %% Special case for boolean arguments: if there is no argument we %% set the value to 'true'. [{Name, true} | OptAcc]; integer -> %% Special case for integer arguments: if the option had not been set %% before we set the value to 1; if not we increment the previous value %% the option had. This is needed to support options like "-vvv" to %% return something like {verbose, 3}. case OptAcc of [{Name, Count} | Tail] -> [{Name, Count + 1} | Tail]; _ -> [{Name, 1} | OptAcc] end; _ -> throw({error, {missing_option_arg, Name}}) end. %% @doc Retrieve the data type form an argument specification. -spec arg_spec_type(arg_spec()) -> arg_type() | undefined. arg_spec_type({Type, _DefaultArg}) -> Type; arg_spec_type(Type) when is_atom(Type) -> Type. %% @doc Convert an argument string to its corresponding data type. -spec to_type(arg_spec() | arg_type(), string()) -> arg_value(). to_type({Type, _DefaultArg}, Arg) -> to_type(Type, Arg); to_type(binary, Arg) -> list_to_binary(Arg); to_type(atom, Arg) -> list_to_atom(Arg); to_type(integer, Arg) -> list_to_integer(Arg); to_type(float, Arg) -> list_to_float(Arg); to_type(boolean, Arg) -> LowerArg = string:to_lower(Arg), case is_arg_true(LowerArg) of true -> true; _ -> case is_arg_false(LowerArg) of true -> false; false -> erlang:error(badarg) end end; to_type(_Type, Arg) -> Arg. -spec is_arg_true(string()) -> boolean(). is_arg_true(Arg) -> (Arg =:= "true") orelse (Arg =:= "t") orelse (Arg =:= "yes") orelse (Arg =:= "y") orelse (Arg =:= "on") orelse (Arg =:= "enabled") orelse (Arg =:= "1"). -spec is_arg_false(string()) -> boolean(). is_arg_false(Arg) -> (Arg =:= "false") orelse (Arg =:= "f") orelse (Arg =:= "no") orelse (Arg =:= "n") orelse (Arg =:= "off") orelse (Arg =:= "disabled") orelse (Arg =:= "0"). -spec is_valid_arg(arg_spec(), nonempty_string()) -> boolean(). is_valid_arg({Type, _DefaultArg}, Arg) -> is_valid_arg(Type, Arg); is_valid_arg(boolean, Arg) -> is_boolean_arg(Arg); is_valid_arg(integer, Arg) -> is_non_neg_integer_arg(Arg); is_valid_arg(float, Arg) -> is_non_neg_float_arg(Arg); is_valid_arg(_Type, _Arg) -> true. -spec is_implicit_arg(arg_spec(), nonempty_string()) -> boolean(). is_implicit_arg({Type, _DefaultArg}, Arg) -> is_implicit_arg(Type, Arg); is_implicit_arg(boolean, Arg) -> not is_boolean_arg(Arg); is_implicit_arg(integer, Arg) -> not is_integer_arg(Arg); is_implicit_arg(_Type, _Arg) -> false. -spec is_boolean_arg(string()) -> boolean(). is_boolean_arg(Arg) -> LowerArg = string:to_lower(Arg), is_arg_true(LowerArg) orelse is_arg_false(LowerArg). -spec is_integer_arg(string()) -> boolean(). is_integer_arg("-" ++ Tail) -> is_non_neg_integer_arg(Tail); is_integer_arg(Arg) -> is_non_neg_integer_arg(Arg). -spec is_non_neg_integer_arg(string()) -> boolean(). is_non_neg_integer_arg([Head | Tail]) when Head >= $0, Head =< $9 -> is_non_neg_integer_arg(Tail); is_non_neg_integer_arg([_Head | _Tail]) -> false; is_non_neg_integer_arg([]) -> true. -spec is_non_neg_float_arg(string()) -> boolean(). is_non_neg_float_arg([Head | Tail]) when (Head >= $0 andalso Head =< $9) orelse Head =:= $. -> is_non_neg_float_arg(Tail); is_non_neg_float_arg([_Head | _Tail]) -> false; is_non_neg_float_arg([]) -> true. %% @doc Show a message on standard_error indicating the command line options and %% arguments that are supported by the program. -spec usage([option_spec()], string()) -> ok. usage(OptSpecList, ProgramName) -> usage(OptSpecList, ProgramName, standard_error). %% @doc Show a message on standard_error or standard_io indicating the command line options and %% arguments that are supported by the program. -spec usage([option_spec()], string(), output_stream() | string()) -> ok. usage(OptSpecList, ProgramName, OutputStream) when is_atom(OutputStream) -> io:format(OutputStream, "Usage: ~s~s~n~n~s~n", [ProgramName, usage_cmd_line(OptSpecList), usage_options(OptSpecList)]); %% @doc Show a message on standard_error indicating the command line options and %% arguments that are supported by the program. The CmdLineTail argument %% is a string that is added to the end of the usage command line. usage(OptSpecList, ProgramName, CmdLineTail) -> usage(OptSpecList, ProgramName, CmdLineTail, standard_error). %% @doc Show a message on standard_error or standard_io indicating the command line options and %% arguments that are supported by the program. The CmdLineTail argument %% is a string that is added to the end of the usage command line. -spec usage([option_spec()], string(), string(), output_stream() | [{string(), string()}]) -> ok. usage(OptSpecList, ProgramName, CmdLineTail, OutputStream) when is_atom(OutputStream) -> io:format(OutputStream, "Usage: ~s~s ~s~n~n~s~n", [ProgramName, usage_cmd_line(OptSpecList), CmdLineTail, usage_options(OptSpecList)]); %% @doc Show a message on standard_error indicating the command line options and %% arguments that are supported by the program. The CmdLineTail and OptionsTail %% arguments are a string that is added to the end of the usage command line %% and a list of tuples that are added to the end of the options' help lines. usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail) -> usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, standard_error). %% @doc Show a message on standard_error or standard_io indicating the command line options and %% arguments that are supported by the program. The CmdLineTail and OptionsTail %% arguments are a string that is added to the end of the usage command line %% and a list of tuples that are added to the end of the options' help lines. -spec usage([option_spec()], string(), string(), [{string(), string()}], output_stream()) -> ok. usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, OutputStream) -> UsageOptions = lists:foldl( fun ({Prefix, Help}, Acc) -> add_option_help(Prefix, Help, Acc) end, usage_options_reverse(OptSpecList, []), OptionsTail), io:format(OutputStream, "Usage: ~s~s ~s~n~n~s~n", [ProgramName, usage_cmd_line(OptSpecList), CmdLineTail, lists:flatten(lists:reverse(UsageOptions))]). %% @doc Return a string with the syntax for the command line options and %% arguments. -spec usage_cmd_line([option_spec()]) -> string(). usage_cmd_line(OptSpecList) -> usage_cmd_line(OptSpecList, []). usage_cmd_line([{Name, Short, Long, ArgSpec, _Help} | Tail], Acc) -> CmdLine = case ArgSpec of undefined -> if %% For options with short form and no argument. Short =/= undefined -> [$\s, $[, $-, Short, $]]; %% For options with only long form and no argument. Long =/= undefined -> [$\s, $[, $-, $-, Long, $]]; true -> [] end; _ -> if %% For options with short form and argument. Short =/= undefined -> [$\s, $[, $-, Short, $\s, $<, atom_to_list(Name), $>, $]]; %% For options with only long form and argument. Long =/= undefined -> [$\s, $[, $-, $-, Long, $\s, $<, atom_to_list(Name), $>, $]]; %% For options with neither short nor long form and argument. true -> [$\s, $<, atom_to_list(Name), $>] end end, usage_cmd_line(Tail, [CmdLine | Acc]); usage_cmd_line([], Acc) -> lists:flatten(lists:reverse(Acc)). %% @doc Return a string with the help message for each of the options and %% arguments. -spec usage_options([option_spec()]) -> string(). usage_options(OptSpecList) -> lists:flatten(lists:reverse(usage_options_reverse(OptSpecList, []))). usage_options_reverse([{Name, Short, Long, _ArgSpec, Help} | Tail], Acc) -> Prefix = case Long of undefined -> case Short of %% Neither short nor long form (non-option argument). undefined -> [$<, atom_to_list(Name), $>]; %% Only short form. _ -> [$-, Short] end; _ -> case Short of %% Only long form. undefined -> [$-, $- | Long]; %% Both short and long form. _ -> [$-, Short, $,, $\s, $-, $- | Long] end end, usage_options_reverse(Tail, add_option_help(Prefix, Help, Acc)); usage_options_reverse([], Acc) -> Acc. %% @doc Add the help message corresponding to an option specification to a list %% with the correct indentation. -spec add_option_help(Prefix :: string(), Help :: string(), Acc :: string()) -> string(). add_option_help(Prefix, Help, Acc) when is_list(Help), Help =/= [] -> FlatPrefix = lists:flatten(Prefix), case ((?INDENTATION * ?TAB_LENGTH) - 2 - length(FlatPrefix)) of TabSize when TabSize > 0 -> Tab = lists:duplicate(ceiling(TabSize / ?TAB_LENGTH), $\t), [[$\s, $\s, FlatPrefix, Tab, Help, $\n] | Acc]; _ -> % The indentation for the option description is 3 tabs (i.e. 24 characters) % IMPORTANT: Change the number of tabs below if you change the % value of the INDENTATION macro. [[$\t, $\t, $\t, Help, $\n], [$\s, $\s, FlatPrefix, $\n] | Acc] end; add_option_help(_Opt, _Prefix, Acc) -> Acc. %% @doc Return the smallest integral value not less than the argument. -spec ceiling(float()) -> integer(). ceiling(X) -> T = erlang:trunc(X), case (X - T) of % Neg when Neg < 0 -> % T; Pos when Pos > 0 -> T + 1; _ -> T end. rebar-2.0.0/src/mustache.erl000066400000000000000000000170741176314626300157320ustar00rootroot00000000000000%% The MIT License %% %% Copyright (c) 2009 Tom Preston-Werner %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% See the README at http://github.com/mojombo/mustache.erl for additional %% documentation and usage examples. -module(mustache). %% v0.1.0 -author("Tom Preston-Werner"). -export([compile/1, compile/2, render/1, render/2, render/3, get/2, get/3, escape/1, start/1]). -record(mstate, {mod = undefined, section_re = undefined, tag_re = undefined}). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. compile(Body) when is_list(Body) -> State = #mstate{}, CompiledTemplate = pre_compile(Body, State), % io:format("~p~n~n", [CompiledTemplate]), % io:format(CompiledTemplate ++ "~n", []), {ok, Tokens, _} = erl_scan:string(CompiledTemplate), {ok, [Form]} = erl_parse:parse_exprs(Tokens), Bindings = erl_eval:new_bindings(), {value, Fun, _} = erl_eval:expr(Form, Bindings), Fun; compile(Mod) -> TemplatePath = template_path(Mod), compile(Mod, TemplatePath). compile(Mod, File) -> code:purge(Mod), {module, _} = code:load_file(Mod), {ok, TemplateBin} = file:read_file(File), Template = re:replace(TemplateBin, "\"", "\\\\\"", [global, {return,list}]), State = #mstate{mod = Mod}, CompiledTemplate = pre_compile(Template, State), % io:format("~p~n~n", [CompiledTemplate]), % io:format(CompiledTemplate ++ "~n", []), {ok, Tokens, _} = erl_scan:string(CompiledTemplate), {ok, [Form]} = erl_parse:parse_exprs(Tokens), Bindings = erl_eval:new_bindings(), {value, Fun, _} = erl_eval:expr(Form, Bindings), Fun. render(Mod) -> TemplatePath = template_path(Mod), render(Mod, TemplatePath). render(Body, Ctx) when is_list(Body) -> TFun = compile(Body), render(undefined, TFun, Ctx); render(Mod, File) when is_list(File) -> render(Mod, File, dict:new()); render(Mod, CompiledTemplate) -> render(Mod, CompiledTemplate, dict:new()). render(Mod, File, Ctx) when is_list(File) -> CompiledTemplate = compile(Mod, File), render(Mod, CompiledTemplate, Ctx); render(Mod, CompiledTemplate, Ctx) -> Ctx2 = dict:store('__mod__', Mod, Ctx), lists:flatten(CompiledTemplate(Ctx2)). pre_compile(T, State) -> SectionRE = "\{\{\#([^\}]*)}}\s*(.+?){{\/\\1\}\}\s*", {ok, CompiledSectionRE} = re:compile(SectionRE, [dotall]), TagRE = "\{\{(#|=|!|<|>|\{)?(.+?)\\1?\}\}+", {ok, CompiledTagRE} = re:compile(TagRE, [dotall]), State2 = State#mstate{section_re = CompiledSectionRE, tag_re = CompiledTagRE}, "fun(Ctx) -> " ++ "CFun = fun(A, B) -> A end, " ++ compiler(T, State2) ++ " end.". compiler(T, State) -> Res = re:run(T, State#mstate.section_re), case Res of {match, [{M0, M1}, {N0, N1}, {C0, C1}]} -> Front = string:substr(T, 1, M0), Back = string:substr(T, M0 + M1 + 1), Name = string:substr(T, N0 + 1, N1), Content = string:substr(T, C0 + 1, C1), "[" ++ compile_tags(Front, State) ++ " | [" ++ compile_section(Name, Content, State) ++ " | [" ++ compiler(Back, State) ++ "]]]"; nomatch -> compile_tags(T, State) end. compile_section(Name, Content, State) -> Mod = State#mstate.mod, Result = compiler(Content, State), "fun() -> " ++ "case mustache:get(" ++ Name ++ ", Ctx, " ++ atom_to_list(Mod) ++ ") of " ++ "true -> " ++ Result ++ "; " ++ "false -> " ++ "[]; " ++ "List when is_list(List) -> " ++ "[fun(Ctx) -> " ++ Result ++ " end(dict:merge(CFun, SubCtx, Ctx)) || SubCtx <- List]; " ++ "Else -> " ++ "throw({template, io_lib:format(\"Bad context for ~p: ~p\", [" ++ Name ++ ", Else])}) " ++ "end " ++ "end()". compile_tags(T, State) -> Res = re:run(T, State#mstate.tag_re), case Res of {match, [{M0, M1}, K, {C0, C1}]} -> Front = string:substr(T, 1, M0), Back = string:substr(T, M0 + M1 + 1), Content = string:substr(T, C0 + 1, C1), Kind = tag_kind(T, K), Result = compile_tag(Kind, Content, State), "[\"" ++ Front ++ "\" | [" ++ Result ++ " | " ++ compile_tags(Back, State) ++ "]]"; nomatch -> "[\"" ++ T ++ "\"]" end. tag_kind(_T, {-1, 0}) -> none; tag_kind(T, {K0, K1}) -> string:substr(T, K0 + 1, K1). compile_tag(none, Content, State) -> Mod = State#mstate.mod, "mustache:escape(mustache:get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ "))"; compile_tag("{", Content, State) -> Mod = State#mstate.mod, "mustache:get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ ")"; compile_tag("!", _Content, _State) -> "[]". template_path(Mod) -> ModPath = code:which(Mod), re:replace(ModPath, "\.beam$", ".mustache", [{return, list}]). get(Key, Ctx) when is_list(Key) -> {ok, Mod} = dict:find('__mod__', Ctx), get(list_to_atom(Key), Ctx, Mod); get(Key, Ctx) -> {ok, Mod} = dict:find('__mod__', Ctx), get(Key, Ctx, Mod). get(Key, Ctx, Mod) when is_list(Key) -> get(list_to_atom(Key), Ctx, Mod); get(Key, Ctx, Mod) -> case dict:find(Key, Ctx) of {ok, Val} -> % io:format("From Ctx {~p, ~p}~n", [Key, Val]), to_s(Val); error -> case erlang:function_exported(Mod, Key, 1) of true -> Val = to_s(Mod:Key(Ctx)), % io:format("From Mod/1 {~p, ~p}~n", [Key, Val]), Val; false -> case erlang:function_exported(Mod, Key, 0) of true -> Val = to_s(Mod:Key()), % io:format("From Mod/0 {~p, ~p}~n", [Key, Val]), Val; false -> [] end end end. to_s(Val) when is_integer(Val) -> integer_to_list(Val); to_s(Val) when is_float(Val) -> io_lib:format("~.2f", [Val]); to_s(Val) when is_atom(Val) -> atom_to_list(Val); to_s(Val) -> Val. escape(HTML) -> escape(HTML, []). escape([], Acc) -> lists:reverse(Acc); escape(["<" | Rest], Acc) -> escape(Rest, lists:reverse("<", Acc)); escape([">" | Rest], Acc) -> escape(Rest, lists:reverse(">", Acc)); escape(["&" | Rest], Acc) -> escape(Rest, lists:reverse("&", Acc)); escape([X | Rest], Acc) -> escape(Rest, [X | Acc]). %%--------------------------------------------------------------------------- start([T]) -> Out = render(list_to_atom(T)), io:format(Out ++ "~n", []). -ifdef(TEST). simple_test() -> Ctx = dict:from_list([{name, "world"}]), Result = render("Hello {{name}}!", Ctx), ?assertEqual("Hello world!", Result). integer_values_too_test() -> Ctx = dict:from_list([{name, "Chris"}, {value, 10000}]), Result = render("Hello {{name}}~nYou have just won ${{value}}!", Ctx), ?assertEqual("Hello Chris~nYou have just won $10000!", Result). -endif. rebar-2.0.0/src/rebar.erl000066400000000000000000000336521176314626300152140ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar). -export([main/1, help/0, parse_args/1, version/0]). -include("rebar.hrl"). -ifndef(BUILD_TIME). -define(BUILD_TIME, "undefined"). -endif. -ifndef(VCS_INFO). -define(VCS_INFO, "undefined"). -endif. -ifndef(OTP_INFO). -define(OTP_INFO, "undefined"). -endif. %% ==================================================================== %% Public API %% ==================================================================== main(Args) -> case catch(run(Args)) of ok -> ok; {error, failed} -> halt(1); Error -> %% Nothing should percolate up from rebar_core; %% Dump this error to console io:format("Uncaught error in rebar_core: ~p\n", [Error]), halt(1) end. %% ==================================================================== %% Internal functions %% ==================================================================== run(RawArgs) -> %% Pre-load the rebar app so that we get default configuration ok = application:load(rebar), %% Parse out command line arguments -- what's left is a list of commands to %% run -- and start running commands Args = parse_args(RawArgs), case rebar_config:get_global(enable_profiling, false) of true -> io:format("Profiling!\n"), try fprof:apply(fun(A) -> run_aux(A) end, [Args]) after fprof:profile(), fprof:analyse([{dest, "fprof.analysis"}]) end; _ -> run_aux(Args) end. run_aux(["help"]) -> help(), ok; run_aux(["version"]) -> %% Display vsn and build time info version(), ok; run_aux(Commands) -> %% Make sure crypto is running ok = crypto:start(), %% Initialize logging system rebar_log:init(), %% Initialize vsn cache _VsnCacheTab = ets:new(rebar_vsn_cache,[named_table, public]), %% Convert command strings to atoms CommandAtoms = [list_to_atom(C) || C <- Commands], %% Determine the location of the rebar executable; important for pulling %% resources out of the escript rebar_config:set_global(escript, filename:absname(escript:script_name())), ?DEBUG("Rebar location: ~p\n", [rebar_config:get_global(escript, undefined)]), %% Note the top-level directory for reference rebar_config:set_global(base_dir, filename:absname(rebar_utils:get_cwd())), %% Keep track of how many operations we do, so we can detect bad commands erlang:put(operations, 0), %% If $HOME/.rebar/config exists load and use as global config GlobalConfigFile = filename:join([os:getenv("HOME"), ".rebar", "config"]), GlobalConfig = case filelib:is_regular(GlobalConfigFile) of true -> ?DEBUG("Load global config file ~p~n", [GlobalConfigFile]), rebar_config:new(GlobalConfigFile); false -> rebar_config:new() end, BaseConfig = rebar_config:base_config(GlobalConfig), %% Process each command, resetting any state between each one rebar_core:process_commands(CommandAtoms, BaseConfig). %% %% print help/usage string %% help() -> OptSpecList = option_spec_list(), getopt:usage(OptSpecList, "rebar", "[var=value,...] ", [{"var=value", "rebar global variables (e.g. force=1)"}, {"command", "Command to run (e.g. compile)"}]). %% %% Parse command line arguments using getopt and also filtering out any %% key=value pairs. What's left is the list of commands to run %% parse_args(Args) -> %% Parse getopt options OptSpecList = option_spec_list(), case getopt:parse(OptSpecList, Args) of {ok, {Options, NonOptArgs}} -> %% Check options and maybe halt execution ok = show_info_maybe_halt(Options, NonOptArgs), GlobalDefines = proplists:get_all_values(defines, Options), rebar_config:set_global(defines, GlobalDefines), %% Setup profiling flag rebar_config:set_global(enable_profiling, proplists:get_bool(profile, Options)), %% Set global variables based on getopt options set_log_level(Options), set_global_flag(Options, force), DefJobs = rebar_config:get_jobs(), case proplists:get_value(jobs, Options, DefJobs) of DefJobs -> ok; Jobs -> rebar_config:set_global(jobs, Jobs) end, %% Set the rebar config to use case proplists:get_value(config, Options) of undefined -> ok; Conf -> rebar_config:set_global(config, Conf) end, %% Filter all the flags (i.e. strings of form key=value) from the %% command line arguments. What's left will be the commands to run. unabbreviate_command_names(filter_flags(NonOptArgs, [])); {error, {Reason, Data}} -> ?ERROR("~s ~p~n~n", [Reason, Data]), help(), halt(1) end. %% %% set log level based on getopt option %% set_log_level(Options) -> LogLevel = case proplists:get_all_values(verbose, Options) of [] -> rebar_log:default_level(); Verbosities -> lists:last(Verbosities) end, rebar_config:set_global(verbose, LogLevel). %% %% show version information and halt %% version() -> {ok, Vsn} = application:get_key(rebar, vsn), ?CONSOLE("rebar ~s ~s ~s ~s\n", [Vsn, ?OTP_INFO, ?BUILD_TIME, ?VCS_INFO]). %% %% set global flag based on getopt option boolean value %% set_global_flag(Options, Flag) -> Value = case proplists:get_bool(Flag, Options) of true -> "1"; false -> "0" end, rebar_config:set_global(Flag, Value). %% %% show info and maybe halt execution %% show_info_maybe_halt(Opts, NonOptArgs) -> false = show_info_maybe_halt(help, Opts, fun help/0), false = show_info_maybe_halt(commands, Opts, fun commands/0), false = show_info_maybe_halt(version, Opts, fun version/0), case NonOptArgs of [] -> ?CONSOLE("No command to run specified!~n",[]), help(), halt(1); _ -> ok end. show_info_maybe_halt(O, Opts, F) -> case proplists:get_bool(O, Opts) of true -> F(), rebar_utils:delayed_halt(0); false -> false end. %% %% print known commands %% commands() -> S = <<" clean Clean compile Compile sources create template= [var=foo,...] Create skel based on template and vars create-app [appid=myapp] Create simple app skel create-node [nodeid=mynode] Create simple node skel list-templates List available templates doc Generate Erlang program documentation check-deps Display to be fetched dependencies get-deps Fetch dependencies update-deps Update fetched dependencies delete-deps Delete fetched dependencies list-deps List dependencies generate [dump_spec=0/1] Build release with reltool overlay Run reltool overlays only generate-upgrade previous_release=path Build an upgrade package generate-appups previous_release=path Generate appup files eunit [suite=foo] Run eunit [test/foo_tests.erl] tests ct [suites=] [case=] Run common_test suites in ./test xref Run cross reference analysis help Show the program options version Show version information ">>, io:put_chars(S). %% %% options accepted via getopt %% option_spec_list() -> Jobs = rebar_config:get_jobs(), JobsHelp = io_lib:format( "Number of concurrent workers a command may use. Default: ~B", [Jobs]), VerboseHelp = "Verbosity level (-v, -vv, -vvv, --verbose 3). Default: 0", [ %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} {help, $h, "help", undefined, "Show the program options"}, {commands, $c, "commands", undefined, "Show available commands"}, {verbose, $v, "verbose", integer, VerboseHelp}, {version, $V, "version", undefined, "Show version information"}, {force, $f, "force", undefined, "Force"}, {defines, $D, undefined, string, "Define compiler macro"}, {jobs, $j, "jobs", integer, JobsHelp}, {config, $C, "config", string, "Rebar config file to use"}, {profile, $p, "profile", undefined, "Profile this run of rebar"} ]. %% %% Seperate all commands (single-words) from flags (key=value) and store %% values into the rebar_config global storage. %% filter_flags([], Commands) -> lists:reverse(Commands); filter_flags([Item | Rest], Commands) -> case string:tokens(Item, "=") of [Command] -> filter_flags(Rest, [Command | Commands]); [KeyStr, RawValue] -> Key = list_to_atom(KeyStr), Value = case Key of verbose -> list_to_integer(RawValue); _ -> RawValue end, rebar_config:set_global(Key, Value), filter_flags(Rest, Commands); Other -> ?CONSOLE("Ignoring command line argument: ~p\n", [Other]), filter_flags(Rest, Commands) end. command_names() -> ["check-deps", "clean", "compile", "create", "create-app", "create-node", "ct", "delete-deps", "doc", "eunit", "generate", "generate-appups", "generate-upgrade", "get-deps", "help", "list-deps", "list-templates", "update-deps", "overlay", "version", "xref"]. unabbreviate_command_names([]) -> []; unabbreviate_command_names([Command | Commands]) -> case get_command_name_candidates(Command) of [] -> %% let the rest of the code detect that the command doesn't exist %% (this would perhaps be a good place to fail) [Command | unabbreviate_command_names(Commands)]; [FullCommand] -> [FullCommand | unabbreviate_command_names(Commands)]; Candidates -> ?ABORT("Found more than one match for abbreviated command name " " '~s',~nplease be more specific. Possible candidates:~n" " ~s~n", [Command, string:join(Candidates, ", ")]) end. get_command_name_candidates(Command) -> %% Get the command names which match the given (abbreviated) command name. %% * "c" matches commands like compile, clean and create-app %% * "create" matches command create only, since it's unique %% * "create-" matches commands starting with create- %% * "c-a" matches create-app %% * "create-a" matches create-app %% * "c-app" matches create-app Candidates = [Candidate || Candidate <- command_names(), is_command_name_candidate(Command, Candidate)], %% Is there a complete match? If so return only that, return a %% list of candidates otherwise case lists:member(Command, Candidates) of true -> [Command]; false -> Candidates end. is_command_name_candidate(Command, Candidate) -> lists:prefix(Command, Candidate) orelse is_command_name_sub_word_candidate(Command, Candidate). is_command_name_sub_word_candidate(Command, Candidate) -> %% Allow for parts of commands to be abbreviated, i.e. create-app %% can be shortened to "create-a", "c-a" or "c-app" (but not %% "create-" since that would be ambiguous). ReOpts = [{return, list}], CommandSubWords = re:split(Command, "-", ReOpts), CandidateSubWords = re:split(Candidate, "-", ReOpts), is_command_name_sub_word_candidate_aux(CommandSubWords, CandidateSubWords). is_command_name_sub_word_candidate_aux([CmdSW | CmdSWs], [CandSW | CandSWs]) -> lists:prefix(CmdSW, CandSW) andalso is_command_name_sub_word_candidate_aux(CmdSWs, CandSWs); is_command_name_sub_word_candidate_aux([], []) -> true; is_command_name_sub_word_candidate_aux(_CmdSWs, _CandSWs) -> false. rebar-2.0.0/src/rebar_abnfc_compiler.erl000066400000000000000000000102331176314626300202250ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2010 Anthony Ramine (nox@dev-extend.eu), %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% The rebar_abnfc_compiler module is a plugin for rebar that compiles %% ABNF grammars into parsers. By default, it compiles all src/*.abnf %% to src/*.erl. %% %% Configuration options should be placed in rebar.config under %% 'abnfc_opts'. Available options include: %% %% doc_root: where to find the ABNF grammars to compile %% "src" by default %% %% out_dir: where to put the generated files. %% "src" by default %% %% source_ext: the file extension the ABNF grammars have. %% ".abnf" by default %% %% module_ext: characters to append to the parser's module name %% "" by default -module(rebar_abnfc_compiler). -export([compile/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== compile(Config, _AppFile) -> DtlOpts = abnfc_opts(Config), rebar_base_compiler:run(Config, [], option(doc_root, DtlOpts), option(source_ext, DtlOpts), option(out_dir, DtlOpts), option(module_ext, DtlOpts) ++ ".erl", fun compile_abnfc/3). %% =================================================================== %% Internal functions %% =================================================================== abnfc_opts(Config) -> rebar_config:get(Config, abnfc_opts, []). option(Opt, DtlOpts) -> proplists:get_value(Opt, DtlOpts, default(Opt)). default(doc_root) -> "src"; default(out_dir) -> "src"; default(source_ext) -> ".abnf"; default(module_ext) -> "". abnfc_is_present() -> code:which(abnfc) =/= non_existing. compile_abnfc(Source, _Target, Config) -> case abnfc_is_present() of false -> ?ERROR("~n===============================================~n" " You need to install abnfc to compile ABNF grammars~n" " Download the latest tarball release from github~n" " https://github.com/nygge/abnfc~n" " and install it into your erlang library dir~n" "===============================================~n~n", []), ?FAIL; true -> AbnfcOpts = abnfc_opts(Config), SourceExt = option(source_ext, AbnfcOpts), Opts = [noobj, {o, option(out_dir, AbnfcOpts)}, {mod, filename:basename(Source, SourceExt) ++ option(module_ext, AbnfcOpts)}], case abnfc:file(Source, Opts) of ok -> ok; Error -> ?ERROR("Compiling grammar ~s failed:~n ~p~n", [Source, Error]), ?FAIL end end. rebar-2.0.0/src/rebar_app_utils.erl000066400000000000000000000140231176314626300172630ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_app_utils). -export([is_app_dir/0, is_app_dir/1, is_app_src/1, app_src_to_app/1, app_name/1, app_applications/1, app_vsn/1, is_skipped_app/1]). -export([load_app_file/1]). % TEMPORARY -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== is_app_dir() -> is_app_dir(rebar_utils:get_cwd()). is_app_dir(Dir) -> SrcDir = filename:join([Dir, "src"]), AppSrc = filename:join([SrcDir, "*.app.src"]), case filelib:wildcard(AppSrc) of [AppSrcFile] -> {true, AppSrcFile}; [] -> EbinDir = filename:join([Dir, "ebin"]), App = filename:join([EbinDir, "*.app"]), case filelib:wildcard(App) of [AppFile] -> {true, AppFile}; [] -> false; _ -> ?ERROR("More than one .app file in ~s~n", [EbinDir]), false end; _ -> ?ERROR("More than one .app.src file in ~s~n", [SrcDir]), false end. is_app_src(Filename) -> %% If removing the extension .app.src yields a shorter name, %% this is an .app.src file. Filename =/= filename:rootname(Filename, ".app.src"). app_src_to_app(Filename) -> filename:join("ebin", filename:basename(Filename, ".app.src") ++ ".app"). app_name(AppFile) -> case load_app_file(AppFile) of {ok, AppName, _} -> AppName; {error, Reason} -> ?ABORT("Failed to extract name from ~s: ~p\n", [AppFile, Reason]) end. app_applications(AppFile) -> case load_app_file(AppFile) of {ok, _, AppInfo} -> get_value(applications, AppInfo, AppFile); {error, Reason} -> ?ABORT("Failed to extract applications from ~s: ~p\n", [AppFile, Reason]) end. app_vsn(AppFile) -> case load_app_file(AppFile) of {ok, _, AppInfo} -> AppDir = filename:dirname(filename:dirname(AppFile)), rebar_utils:vcs_vsn(get_value(vsn, AppInfo, AppFile), AppDir); {error, Reason} -> ?ABORT("Failed to extract vsn from ~s: ~p\n", [AppFile, Reason]) end. is_skipped_app(AppFile) -> ThisApp = app_name(AppFile), %% Check for apps global parameter; this is a comma-delimited list %% of apps on which we want to run commands case get_apps() of undefined -> %% No apps parameter specified, check the skip_apps list.. case get_skip_apps() of undefined -> %% No skip_apps list, run everything.. false; SkipApps -> TargetApps = [list_to_atom(A) || A <- string:tokens(SkipApps, ",")], is_skipped_app(ThisApp, TargetApps) end; Apps -> %% run only selected apps TargetApps = [list_to_atom(A) || A <- string:tokens(Apps, ",")], is_selected_app(ThisApp, TargetApps) end. %% =================================================================== %% Internal functions %% =================================================================== load_app_file(Filename) -> AppFile = {app_file, Filename}, case erlang:get(AppFile) of undefined -> case file:consult(Filename) of {ok, [{application, AppName, AppData}]} -> erlang:put(AppFile, {AppName, AppData}), {ok, AppName, AppData}; {error, _} = Error -> Error; Other -> {error, {unexpected_terms, Other}} end; {AppName, AppData} -> {ok, AppName, AppData} end. 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]); Value -> Value end. %% apps= for selecting apps is_selected_app(ThisApp, TargetApps) -> case lists:member(ThisApp, TargetApps) of false -> {true, ThisApp}; true -> false end. %% skip_apps= for filtering apps is_skipped_app(ThisApp, TargetApps) -> case lists:member(ThisApp, TargetApps) of false -> false; true -> {true, ThisApp} end. get_apps() -> rebar_utils:get_deprecated_global(app, apps, "soon"). get_skip_apps() -> rebar_utils:get_deprecated_global(skip_app, skip_apps, "soon"). rebar-2.0.0/src/rebar_appups.erl000066400000000000000000000174011176314626300165760ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2011 Joe Williams (joe@joetify.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------ -module(rebar_appups). -include("rebar.hrl"). -export(['generate-appups'/2]). -define(APPUPFILEFORMAT, "%% appup generated for ~p by rebar (~p)~n" "{~p, [{~p, ~p}], [{~p, []}]}.~n"). %% ==================================================================== %% Public API %% ==================================================================== 'generate-appups'(_Config, ReltoolFile) -> %% Get the old release path ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile), TargetParentDir = rebar_rel_utils:get_target_parent_dir(ReltoolConfig), OldVerPath = filename:join([TargetParentDir, rebar_rel_utils:get_previous_release_path()]), %% Get the new and old release name and versions {Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig), NewVerPath = filename:join([TargetParentDir, Name]), {NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NewVerPath), {OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath), %% Run some simple checks true = rebar_utils:prop_check(NewVer =/= OldVer, "New and old .rel versions match~n", []), true = rebar_utils:prop_check( NewName == OldName, "Reltool and .rel release names do not match~n", []), %% Find all the apps that have been upgraded {_Added, _Removed, Upgraded} = get_apps(Name, OldVerPath, NewVerPath), %% Get a list of any appup files that exist in the new release NewAppUpFiles = rebar_utils:find_files( filename:join([NewVerPath, "lib"]), "^.*.appup$"), %% Convert the list of appup files into app names AppUpApps = [file_to_name(File) || File <- NewAppUpFiles], %% Create a list of apps that don't already have appups UpgradeApps = genappup_which_apps(Upgraded, AppUpApps), %% Generate appup files for upgraded apps generate_appup_files(NewVerPath, OldVerPath, UpgradeApps), ok. %% =================================================================== %% Internal functions %% =================================================================== get_apps(Name, OldVerPath, NewVerPath) -> OldApps = rebar_rel_utils:get_rel_apps(Name, OldVerPath), ?DEBUG("Old Version Apps: ~p~n", [OldApps]), NewApps = rebar_rel_utils:get_rel_apps(Name, NewVerPath), ?DEBUG("New Version Apps: ~p~n", [NewApps]), Added = app_list_diff(NewApps, OldApps), ?DEBUG("Added: ~p~n", [Added]), Removed = app_list_diff(OldApps, NewApps), ?DEBUG("Removed: ~p~n", [Removed]), PossiblyUpgraded = proplists:get_keys(NewApps), UpgradedApps = [upgraded_app(AppName, proplists:get_value(AppName, OldApps), proplists:get_value(AppName, NewApps)) || AppName <- PossiblyUpgraded], Upgraded = lists:dropwhile(fun(Elem) -> Elem == false end, lists:sort(UpgradedApps)), ?DEBUG("Upgraded: ~p~n", [Upgraded]), {Added, Removed, Upgraded}. upgraded_app(AppName, OldAppVer, NewAppVer) when OldAppVer /= NewAppVer -> {AppName, {OldAppVer, NewAppVer}}; upgraded_app(_, _, _) -> false. app_list_diff(List1, List2) -> List3 = lists:umerge(lists:sort(proplists:get_keys(List1)), lists:sort(proplists:get_keys(List2))), List3 -- proplists:get_keys(List2). file_to_name(File) -> filename:rootname(filename:basename(File)). genappup_which_apps(UpgradedApps, [First|Rest]) -> List = proplists:delete(list_to_atom(First), UpgradedApps), genappup_which_apps(List, Rest); genappup_which_apps(Apps, []) -> Apps. generate_appup_files(NewVerPath, OldVerPath, [{_App, {undefined, _}}|Rest]) -> generate_appup_files(NewVerPath, OldVerPath, Rest); generate_appup_files(NewVerPath, OldVerPath, [{App, {OldVer, NewVer}}|Rest]) -> OldEbinDir = filename:join([OldVerPath, "lib", atom_to_list(App) ++ "-" ++ OldVer, "ebin"]), NewEbinDir = filename:join([NewVerPath, "lib", atom_to_list(App) ++ "-" ++ NewVer, "ebin"]), {AddedFiles, DeletedFiles, ChangedFiles} = beam_lib:cmp_dirs(NewEbinDir, OldEbinDir), Added = [generate_instruction(added, File) || File <- AddedFiles], Deleted = [generate_instruction(deleted, File) || File <- DeletedFiles], Changed = [generate_instruction(changed, File) || File <- ChangedFiles], Inst = lists:append([Added, Deleted, Changed]), AppUpFile = filename:join([NewEbinDir, atom_to_list(App) ++ ".appup"]), ok = file:write_file(AppUpFile, io_lib:fwrite(?APPUPFILEFORMAT, [App, rebar_utils:now_str(), NewVer, OldVer, Inst, OldVer])), ?CONSOLE("Generated appup for ~p~n", [App]), generate_appup_files(NewVerPath, OldVerPath, Rest); generate_appup_files(_, _, []) -> ?CONSOLE("Appup generation complete~n", []). generate_instruction(added, File) -> Name = list_to_atom(file_to_name(File)), {add_module, Name}; generate_instruction(deleted, File) -> Name = list_to_atom(file_to_name(File)), {delete_module, Name}; generate_instruction(changed, {File, _}) -> {ok, {Name, List}} = beam_lib:chunks(File, [attributes, exports]), Behavior = get_behavior(List), CodeChange = is_code_change(List), generate_instruction_advanced(Name, Behavior, CodeChange). generate_instruction_advanced(Name, undefined, undefined) -> %% Not a behavior or code change, assume purely functional {load_module, Name}; generate_instruction_advanced(Name, [supervisor], _) -> %% Supervisor {update, Name, supervisor}; generate_instruction_advanced(Name, _, code_change) -> %% Includes code_change export {update, Name, {advanced, []}}; generate_instruction_advanced(Name, _, _) -> %% Anything else {load_module, Name}. get_behavior(List) -> Attributes = proplists:get_value(attributes, List), case proplists:get_value(behavior, Attributes) of undefined -> proplists:get_value(behaviour, Attributes); Else -> Else end. is_code_change(List) -> Exports = proplists:get_value(exports, List), case proplists:is_defined(code_change, Exports) of true -> code_change; false -> undefined end. rebar-2.0.0/src/rebar_asn1_compiler.erl000066400000000000000000000064061176314626300200250ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_asn1_compiler). -author('ruslan@babayev.com'). -export([compile/2, clean/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== -spec compile(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. compile(Config, _AppFile) -> rebar_base_compiler:run(Config, filelib:wildcard("asn1/*.asn1"), "asn1", ".asn1", "src", ".erl", fun compile_asn1/3). -spec clean(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. clean(_Config, _AppFile) -> GeneratedFiles = asn_generated_files("asn1", "src", "include"), ok = rebar_file_utils:delete_each(GeneratedFiles), ok. -spec compile_asn1(file:filename(), file:filename(), rebar_config:config()) -> ok. compile_asn1(Source, Target, Config) -> ok = filelib:ensure_dir(Target), ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")), Opts = [{outdir, "src"}, noobj] ++ rebar_config:get(Config, asn1_opts, []), case asn1ct:compile(Source, Opts) of ok -> Asn1 = filename:basename(Source, ".asn1"), HrlFile = filename:join("src", Asn1 ++ ".hrl"), case filelib:is_regular(HrlFile) of true -> ok = rebar_file_utils:mv(HrlFile, "include"); false -> ok end; {error, _Reason} -> ?FAIL end. asn_generated_files(AsnDir, SrcDir, IncDir) -> lists:foldl( fun(AsnFile, Acc) -> Base = filename:rootname(filename:basename(AsnFile)), [filename:join([IncDir, Base ++ ".hrl"])| filelib:wildcard(filename:join([SrcDir, Base ++ ".*"]))] ++ Acc end, [], filelib:wildcard(filename:join([AsnDir, "*.asn1"])) ). rebar-2.0.0/src/rebar_base_compiler.erl000066400000000000000000000151001176314626300200640ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_base_compiler). -include("rebar.hrl"). -export([run/4, run/7, run/8]). %% =================================================================== %% Public API %% =================================================================== run(Config, FirstFiles, RestFiles, CompileFn) -> %% Compile the first files in sequence compile_each(FirstFiles, Config, CompileFn), %% Spin up workers for the rest of the files case RestFiles of [] -> ok; _ -> Self = self(), F = fun() -> compile_worker(Self, Config, CompileFn) end, Jobs = rebar_config:get_jobs(), ?DEBUG("Starting ~B compile worker(s)~n", [Jobs]), Pids = [spawn_monitor(F) || _I <- lists:seq(1,Jobs)], compile_queue(Pids, RestFiles) end. run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn) -> run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, [check_last_mod]). run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, Opts) -> %% Convert simple extension to proper regex SourceExtRe = ".*\\" ++ SourceExt ++ [$$], %% Find all possible source files FoundFiles = rebar_utils:find_files(SourceDir, SourceExtRe), %% Remove first files from found files RestFiles = [Source || Source <- FoundFiles, not lists:member(Source, FirstFiles)], %% Check opts for flag indicating that compile should check lastmod CheckLastMod = proplists:get_bool(check_last_mod, Opts), run(Config, FirstFiles, RestFiles, fun(S, C) -> Target = target_file(S, SourceDir, SourceExt, TargetDir, TargetExt), simple_compile_wrapper(S, Target, Compile3Fn, C, CheckLastMod) end). %% =================================================================== %% Internal functions %% =================================================================== simple_compile_wrapper(Source, Target, Compile3Fn, Config, false) -> Compile3Fn(Source, Target, Config); simple_compile_wrapper(Source, Target, Compile3Fn, Config, true) -> case filelib:last_modified(Target) < filelib:last_modified(Source) of true -> Compile3Fn(Source, Target, Config); false -> skipped end. target_file(SourceFile, SourceDir, SourceExt, TargetDir, TargetExt) -> %% Remove all leading components of the source dir from the file -- we want %% to maintain the deeper structure (if any) of the source file path BaseFile = remove_common_path(SourceFile, SourceDir), filename:join([TargetDir, filename:dirname(BaseFile), filename:basename(BaseFile, SourceExt) ++ TargetExt]). remove_common_path(Fname, Path) -> remove_common_path1(filename:split(Fname), filename:split(Path)). remove_common_path1([Part | RestFilename], [Part | RestPath]) -> remove_common_path1(RestFilename, RestPath); remove_common_path1(FilenameParts, _) -> filename:join(FilenameParts). compile(Source, Config, CompileFn) -> case CompileFn(Source, Config) of ok -> ok; skipped -> skipped end. compile_each([], _Config, _CompileFn) -> ok; compile_each([Source | Rest], Config, CompileFn) -> case compile(Source, Config, CompileFn) of ok -> ?CONSOLE("Compiled ~s\n", [Source]); skipped -> ?INFO("Skipped ~s\n", [Source]) end, compile_each(Rest, Config, CompileFn). compile_queue([], []) -> ok; compile_queue(Pids, Targets) -> receive {next, Worker} -> case Targets of [] -> Worker ! empty, compile_queue(Pids, Targets); [Source | Rest] -> Worker ! {compile, Source}, compile_queue(Pids, Rest) end; {fail, Error} -> ?DEBUG("Worker compilation failed: ~p\n", [Error]), ?FAIL; {compiled, Source} -> ?CONSOLE("Compiled ~s\n", [Source]), compile_queue(Pids, Targets); {skipped, Source} -> ?INFO("Skipped ~s\n", [Source]), compile_queue(Pids, Targets); {'DOWN', Mref, _, Pid, normal} -> ?DEBUG("Worker exited cleanly\n", []), Pids2 = lists:delete({Pid, Mref}, Pids), compile_queue(Pids2, Targets); {'DOWN', _Mref, _, _Pid, Info} -> ?DEBUG("Worker failed: ~p\n", [Info]), ?FAIL end. compile_worker(QueuePid, Config, CompileFn) -> QueuePid ! {next, self()}, receive {compile, Source} -> case catch(compile(Source, Config, CompileFn)) of ok -> QueuePid ! {compiled, Source}, compile_worker(QueuePid, Config, CompileFn); skipped -> QueuePid ! {skipped, Source}, compile_worker(QueuePid, Config, CompileFn); Error -> QueuePid ! {fail, [{error, Error}, {source, Source}]}, ok end; empty -> ok end. rebar-2.0.0/src/rebar_cleaner.erl000066400000000000000000000034541176314626300167020ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_cleaner). -export([clean/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== clean(Config, _AppFile) -> %% Get a list of files to delete from config and remove them FilesToClean = rebar_config:get(Config, clean_files, []), lists:foreach(fun (F) -> rebar_file_utils:rm_rf(F) end, FilesToClean). rebar-2.0.0/src/rebar_config.erl000066400000000000000000000145751176314626300165440ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_config). -export([new/0, new/1, base_config/1, consult_file/1, get/3, get_local/3, get_list/3, get_all/2, set/3, set_global/2, get_global/2, is_verbose/0, get_jobs/0, set_env/3, get_env/2]). -include("rebar.hrl"). -record(config, { dir :: file:filename(), opts = [] :: list(), envs = new_env() :: dict() }). %% Types that can be used from other modules -- alphabetically ordered. -export_type([config/0]). %% data types -opaque config() :: #config{}. %% =================================================================== %% Public API %% =================================================================== base_config(#config{opts=Opts0}) -> ConfName = rebar_config:get_global(config, "rebar.config"), new(Opts0, ConfName). new() -> #config{dir = rebar_utils:get_cwd()}. new(ConfigFile) when is_list(ConfigFile) -> case consult_file(ConfigFile) of {ok, Opts} -> #config { dir = rebar_utils:get_cwd(), opts = Opts }; Other -> ?ABORT("Failed to load ~s: ~p~n", [ConfigFile, Other]) end; new(_ParentConfig=#config{opts=Opts0})-> new(Opts0, "rebar.config"). new(Opts0, ConfName) -> %% Load terms from rebar.config, if it exists Dir = rebar_utils:get_cwd(), ConfigFile = filename:join([Dir, ConfName]), Opts = case consult_file(ConfigFile) of {ok, Terms} -> %% Found a config file with some terms. We need to %% be able to distinguish between local definitions %% (i.e. from the file in the cwd) and inherited %% definitions. To accomplish this, we use a marker %% in the proplist (since order matters) between %% the new and old defs. Terms ++ [local] ++ [Opt || Opt <- Opts0, Opt /= local]; {error, enoent} -> [local] ++ [Opt || Opt <- Opts0, Opt /= local]; Other -> ?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other]) end, #config{dir = Dir, opts = Opts}. get(Config, Key, Default) -> proplists:get_value(Key, Config#config.opts, Default). get_list(Config, Key, Default) -> get(Config, Key, Default). get_local(Config, Key, Default) -> proplists:get_value(Key, local_opts(Config#config.opts, []), Default). get_all(Config, Key) -> proplists:get_all_values(Key, Config#config.opts). set(Config, Key, Value) -> Opts = proplists:delete(Key, Config#config.opts), Config#config { opts = [{Key, Value} | Opts] }. set_global(jobs=Key, Value) when is_list(Value) -> set_global(Key, list_to_integer(Value)); set_global(jobs=Key, Value) when is_integer(Value) -> application:set_env(rebar_global, Key, erlang:max(1, Value)); set_global(Key, Value) -> application:set_env(rebar_global, Key, Value). get_global(Key, Default) -> case application:get_env(rebar_global, Key) of undefined -> Default; {ok, Value} -> Value end. is_verbose() -> DefaulLevel = rebar_log:default_level(), get_global(verbose, DefaulLevel) > DefaulLevel. get_jobs() -> get_global(jobs, 3). consult_file(File) -> case filename:extension(File) of ".script" -> consult_and_eval(remove_script_ext(File), File); _ -> Script = File ++ ".script", case filelib:is_regular(Script) of true -> consult_and_eval(File, Script); false -> ?DEBUG("Consult config file ~p~n", [File]), file:consult(File) end end. set_env(Config, Mod, Env) -> OldEnvs = Config#config.envs, NewEnvs = dict:store(Mod, Env, OldEnvs), Config#config{envs=NewEnvs}. get_env(Config, Mod) -> dict:fetch(Mod, Config#config.envs). %% =================================================================== %% Internal functions %% =================================================================== consult_and_eval(File, Script) -> ?DEBUG("Evaluating config script ~p~n", [Script]), ConfigData = try_consult(File), file:script(Script, bs([{'CONFIG', ConfigData}, {'SCRIPT', Script}])). remove_script_ext(F) -> "tpircs." ++ Rev = lists:reverse(F), lists:reverse(Rev). try_consult(File) -> case file:consult(File) of {ok, Terms} -> ?DEBUG("Consult config file ~p~n", [File]), Terms; {error, enoent} -> []; {error, Reason} -> ?ABORT("Failed to read config file ~s: ~p~n", [File, Reason]) end. bs(Vars) -> lists:foldl(fun({K,V}, Bs) -> erl_eval:add_binding(K, V, Bs) end, erl_eval:new_bindings(), Vars). local_opts([], Acc) -> lists:reverse(Acc); local_opts([local | _Rest], Acc) -> lists:reverse(Acc); local_opts([Item | Rest], Acc) -> local_opts(Rest, [Item | Acc]). new_env() -> dict:new(). rebar-2.0.0/src/rebar_core.erl000066400000000000000000000501301176314626300162120ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_core). -export([process_commands/2, skip_dir/1, is_skip_dir/1, skip_dirs/0]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== skip_dir(Dir) -> SkipDir = {skip_dir, Dir}, case erlang:get(SkipDir) of undefined -> ?DEBUG("Adding skip dir: ~s\n", [Dir]), erlang:put(SkipDir, true); true -> ok end. is_skip_dir(Dir) -> case erlang:get({skip_dir, Dir}) of undefined -> false; true -> true end. skip_dirs() -> [Dir || {{skip_dir, Dir}, true} <- erlang:get()]. %% =================================================================== %% Internal functions %% =================================================================== process_commands([], _ParentConfig) -> case erlang:get(operations) of 0 -> %% none of the commands had an effect ?FAIL; _ -> ok end; process_commands([Command | Rest], ParentConfig) -> %% Reset skip dirs lists:foreach(fun (D) -> erlang:erase({skip_dir, D}) end, skip_dirs()), Operations = erlang:get(operations), %% Convert the code path so that all the entries are absolute paths. %% If not, code:set_path() may choke on invalid relative paths when trying %% to restore the code path from inside a subdirectory. true = rebar_utils:expand_code_path(), _ = process_dir(rebar_utils:get_cwd(), ParentConfig, Command, sets:new()), case erlang:get(operations) of Operations -> %% This command didn't do anything ?CONSOLE("Command '~p' not understood or not applicable~n", [Command]); _ -> ok end, %% Wipe out vsn cache to avoid invalid hits when %% dependencies are updated ets:delete_all_objects(rebar_vsn_cache), process_commands(Rest, ParentConfig). process_dir(Dir, ParentConfig, Command, DirSet) -> case filelib:is_dir(Dir) of false -> ?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]), DirSet; true -> AbsDir = filename:absname(Dir), ShouldPrintDir = not (is_skip_dir(Dir) orelse processing_base_dir(Dir)), case ShouldPrintDir of true -> ?CONSOLE("==> Entering directory `~s'\n", [AbsDir]); _ -> ok end, ok = file:set_cwd(Dir), Config = maybe_load_local_config(Dir, ParentConfig), %% Save the current code path and then update it with %% lib_dirs. Children inherit parents code path, but we %% also want to ensure that we restore everything to pristine %% condition after processing this child CurrentCodePath = update_code_path(Config), %% Get the list of processing modules and check each one against %% CWD to see if it's a fit -- if it is, use that set of modules %% to process this dir. {ok, AvailModuleSets} = application:get_env(rebar, modules), ModuleSet = choose_module_set(AvailModuleSets, Dir), Res = maybe_process_dir(ModuleSet, Config, CurrentCodePath, Dir, Command, DirSet), case ShouldPrintDir of true -> ?CONSOLE("==> Leaving directory `~s'\n", [AbsDir]); false -> ok end, Res end. maybe_process_dir({[], undefined}=ModuleSet, Config, CurrentCodePath, Dir, Command, DirSet) -> process_dir0(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet); maybe_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath, Dir, Command, DirSet) -> case lists:suffix(".app.src", ModuleSetFile) orelse lists:suffix(".app", ModuleSetFile) of true -> %% .app or .app.src file, check if is_skipped_app maybe_process_dir0(ModuleSetFile, ModuleSet, Config, CurrentCodePath, Dir, Command, DirSet); false -> %% not an app dir, no need to consider apps=/skip_apps= process_dir0(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet) end. maybe_process_dir0(AppFile, ModuleSet, Config, CurrentCodePath, Dir, Command, DirSet) -> case rebar_app_utils:is_skipped_app(AppFile) of {true, SkippedApp} -> ?DEBUG("Skipping app: ~p~n", [SkippedApp]), increment_operations(), DirSet; false -> process_dir0(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet) end. process_dir0(Dir, Command, DirSet, Config0, CurrentCodePath, {DirModules, ModuleSetFile}) -> %% Get the list of modules for "any dir". This is a catch-all list %% of modules that are processed in addition to modules associated %% with this directory type. These any_dir modules are processed %% FIRST. {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules), Modules = AnyDirModules ++ DirModules, %% Invoke 'preprocess' on the modules -- this yields a list of other %% directories that should be processed _before_ the current one. Predirs = acc_modules(Modules, preprocess, Config0, ModuleSetFile), SubdirAssoc = remember_cwd_subdir(Dir, Predirs), %% Get the list of plug-in modules from rebar.config. These %% modules may participate in preprocess and postprocess. {ok, PluginModules} = plugin_modules(Config0, SubdirAssoc), PluginPredirs = acc_modules(PluginModules, preprocess, Config0, ModuleSetFile), AllPredirs = Predirs ++ PluginPredirs, ?DEBUG("Predirs: ~p\n", [AllPredirs]), DirSet2 = process_each(AllPredirs, Command, Config0, ModuleSetFile, DirSet), %% Make sure the CWD is reset properly; processing the dirs may have %% caused it to change ok = file:set_cwd(Dir), %% Check that this directory is not on the skip list Config = case is_skip_dir(Dir) of true -> %% Do not execute the command on the directory, as some %% module has requested a skip on it. ?INFO("Skipping ~s in ~s\n", [Command, Dir]), Config0; false -> %% Check for and get command specific environments {Config1, Env} = setup_envs(Config0, Modules), %% Execute any before_command plugins on this directory execute_pre(Command, PluginModules, Config1, ModuleSetFile, Env), %% Execute the current command on this directory execute(Command, Modules ++ PluginModules, Config1, ModuleSetFile, Env), %% Execute any after_command plugins on this directory execute_post(Command, PluginModules, Config1, ModuleSetFile, Env), Config1 end, %% Mark the current directory as processed DirSet3 = sets:add_element(Dir, DirSet2), %% Invoke 'postprocess' on the modules. This yields a list of other %% directories that should be processed _after_ the current one. Postdirs = acc_modules(Modules ++ PluginModules, postprocess, Config, ModuleSetFile), ?DEBUG("Postdirs: ~p\n", [Postdirs]), DirSet4 = process_each(Postdirs, Command, Config, ModuleSetFile, DirSet3), %% Make sure the CWD is reset properly; processing the dirs may have %% caused it to change ok = file:set_cwd(Dir), %% Once we're all done processing, reset the code path to whatever %% the parent initialized it to restore_code_path(CurrentCodePath), %% Return the updated dirset as our result DirSet4. remember_cwd_subdir(Cwd, Subdirs) -> Store = fun(Dir, Dict) -> case dict:find(Dir, Dict) of error -> ?DEBUG("Associate sub_dir ~s with ~s~n", [Dir, Cwd]), dict:store(Dir, Cwd, Dict); {ok, Existing} -> ?ABORT("Internal consistency assertion failed.~n" "sub_dir ~s already associated with ~s.~n" "Duplicate sub_dirs or deps entries?", [Dir, Existing]), Dict end end, lists:foldl(Store, dict:new(), Subdirs). maybe_load_local_config(Dir, ParentConfig) -> %% We need to ensure we don't overwrite custom %% config when we are dealing with base_dir. case processing_base_dir(Dir) of true -> ParentConfig; false -> rebar_config:new(ParentConfig) end. processing_base_dir(Dir) -> Dir == rebar_config:get_global(base_dir, undefined). %% %% Given a list of directories and a set of previously processed directories, %% process each one we haven't seen yet %% process_each([], _Command, _Config, _ModuleSetFile, DirSet) -> DirSet; process_each([Dir | Rest], Command, Config, ModuleSetFile, DirSet) -> case sets:is_element(Dir, DirSet) of true -> ?DEBUG("Skipping ~s; already processed!\n", [Dir]), process_each(Rest, Command, Config, ModuleSetFile, DirSet); false -> DirSet2 = process_dir(Dir, Config, Command, DirSet), process_each(Rest, Command, Config, ModuleSetFile, DirSet2) end. %% %% Given a list of module sets from rebar.app and a directory, find %% the appropriate subset of modules for this directory %% choose_module_set([], _Dir) -> {[], undefined}; choose_module_set([{Type, Modules} | Rest], Dir) -> case is_dir_type(Type, Dir) of {true, File} -> {Modules, File}; false -> choose_module_set(Rest, Dir) end. is_dir_type(app_dir, Dir) -> rebar_app_utils:is_app_dir(Dir); is_dir_type(rel_dir, Dir) -> rebar_rel_utils:is_rel_dir(Dir); is_dir_type(_, _) -> false. execute_pre(Command, Modules, Config, ModuleFile, Env) -> execute_plugin_hook("pre_", Command, Modules, Config, ModuleFile, Env). execute_post(Command, Modules, Config, ModuleFile, Env) -> execute_plugin_hook("post_", Command, Modules, Config, ModuleFile, Env). execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) -> HookFunction = list_to_atom(Hook ++ atom_to_list(Command)), execute(HookFunction, Modules, Config, ModuleFile, Env). %% %% Execute a command across all applicable modules %% execute(Command, Modules, Config, ModuleFile, Env) -> case select_modules(Modules, Command, []) of [] -> Cmd = atom_to_list(Command), case lists:prefix("pre_", Cmd) orelse lists:prefix("post_", Cmd) of true -> ok; false -> ?WARN("'~p' command does not apply to directory ~s\n", [Command, rebar_utils:get_cwd()]) end; TargetModules -> %% Provide some info on where we are Dir = rebar_utils:get_cwd(), ?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]), increment_operations(), %% Run the available modules apply_hooks(pre_hooks, Config, Command, Env), case catch(run_modules(TargetModules, Command, Config, ModuleFile)) of ok -> apply_hooks(post_hooks, Config, Command, Env), ok; {error, failed} -> ?FAIL; {Module, {error, _} = Other} -> ?ABORT("~p failed while processing ~s in module ~s: ~s\n", [Command, Dir, Module, io_lib:print(Other, 1, 80, -1)]); Other -> ?ABORT("~p failed while processing ~s: ~s\n", [Command, Dir, io_lib:print(Other, 1, 80, -1)]) end end. %% Increment the count of operations, since some module %% responds to this command increment_operations() -> erlang:put(operations, erlang:get(operations) + 1). update_code_path(Config) -> case rebar_config:get_local(Config, lib_dirs, []) of [] -> no_change; Paths -> OldPath = code:get_path(), LibPaths = expand_lib_dirs(Paths, rebar_utils:get_cwd(), []), ok = code:add_pathsa(LibPaths), {old, OldPath} end. restore_code_path(no_change) -> ok; restore_code_path({old, Path}) -> %% Verify that all of the paths still exist -- some dynamically %% added paths can get blown away during clean. true = code:set_path([F || F <- Path, filelib:is_file(F)]), ok. expand_lib_dirs([], _Root, Acc) -> Acc; expand_lib_dirs([Dir | Rest], Root, Acc) -> Apps = filelib:wildcard(filename:join([Dir, "*", "ebin"])), FqApps = [filename:join([Root, A]) || A <- Apps], expand_lib_dirs(Rest, Root, Acc ++ FqApps). select_modules([], _Command, Acc) -> lists:reverse(Acc); select_modules([Module | Rest], Command, Acc) -> {module, Module} = code:ensure_loaded(Module), case erlang:function_exported(Module, Command, 2) of true -> select_modules(Rest, Command, [Module | Acc]); false -> select_modules(Rest, Command, Acc) end. run_modules([], _Command, _Config, _File) -> ok; run_modules([Module | Rest], Command, Config, File) -> case Module:Command(Config, File) of ok -> run_modules(Rest, Command, Config, File); {error, _} = Error -> {Module, Error} end. apply_hooks(Mode, Config, Command, Env) -> Hooks = rebar_config:get_local(Config, Mode, []), lists:foreach(fun apply_hook/1, [{Env, Hook} || Hook <- Hooks, element(1, Hook) =:= Command orelse element(2, Hook) =:= Command]). apply_hook({Env, {Arch, Command, Hook}}) -> case rebar_utils:is_arch(Arch) of true -> apply_hook({Env, {Command, Hook}}); false -> ok end; apply_hook({Env, {Command, Hook}}) -> Msg = lists:flatten(io_lib:format("Command [~p] failed!~n", [Command])), rebar_utils:sh(Hook, [{env, Env}, {abort_on_error, Msg}]). setup_envs(Config, Modules) -> lists:foldl(fun(M, {C,E}=T) -> case erlang:function_exported(M, setup_env, 1) of true -> Env = M:setup_env(C), C1 = rebar_config:set_env(C, M, Env), {C1, E++Env}; false -> T end end, {Config, []}, Modules). acc_modules(Modules, Command, Config, File) -> acc_modules(select_modules(Modules, Command, []), Command, Config, File, []). acc_modules([], _Command, _Config, _File, Acc) -> Acc; acc_modules([Module | Rest], Command, Config, File, Acc) -> {ok, Dirs} = Module:Command(Config, File), acc_modules(Rest, Command, Config, File, Acc ++ Dirs). %% %% Return a flat list of rebar plugin modules. %% plugin_modules(Config, SubdirAssoc) -> Modules = lists:flatten(rebar_config:get_all(Config, plugins)), plugin_modules(Config, SubdirAssoc, ulist(Modules)). ulist(L) -> ulist(L, []). ulist([], Acc) -> lists:reverse(Acc); ulist([H | T], Acc) -> case lists:member(H, Acc) of true -> ulist(T, Acc); false -> ulist(T, [H | Acc]) end. plugin_modules(_Config, _SubdirAssoc, []) -> {ok, []}; plugin_modules(Config, SubdirAssoc, Modules) -> FoundModules = [M || M <- Modules, code:which(M) =/= non_existing], plugin_modules(Config, SubdirAssoc, FoundModules, Modules -- FoundModules). plugin_modules(_Config, _SubdirAssoc, FoundModules, []) -> {ok, FoundModules}; plugin_modules(Config, SubdirAssoc, FoundModules, MissingModules) -> {Loaded, NotLoaded} = load_plugin_modules(Config, SubdirAssoc, MissingModules), AllViablePlugins = FoundModules ++ Loaded, case NotLoaded =/= [] of true -> %% NB: we continue to ignore this situation, as did the original code ?WARN("Missing plugins: ~p\n", [NotLoaded]); false -> ?DEBUG("Loaded plugins: ~p~n", [AllViablePlugins]), ok end, {ok, AllViablePlugins}. load_plugin_modules(Config, SubdirAssoc, Modules) -> Cwd = rebar_utils:get_cwd(), PluginDir = case rebar_config:get_local(Config, plugin_dir, undefined) of undefined -> filename:join(Cwd, "plugins"); Dir -> Dir end, %% Find relevant sources in base_dir and plugin_dir Erls = string:join([atom_to_list(M)++"\\.erl" || M <- Modules], "|"), RE = "^" ++ Erls ++ "\$", BaseDir = get_plugin_base_dir(Cwd, SubdirAssoc), %% If a plugin is found both in base_dir and plugin_dir, the clash %% will provoke an error and we'll abort. Sources = rebar_utils:find_files(PluginDir, RE, false) ++ rebar_utils:find_files(BaseDir, RE, false), %% Compile and load plugins Loaded = [load_plugin(Src) || Src <- Sources], FilterMissing = is_missing_plugin(Loaded), NotLoaded = [V || V <- Modules, FilterMissing(V)], {Loaded, NotLoaded}. get_plugin_base_dir(Cwd, SubdirAssoc) -> case dict:find(Cwd, SubdirAssoc) of {ok, BaseDir} -> BaseDir; error -> Cwd end. is_missing_plugin(Loaded) -> fun(Mod) -> not lists:member(Mod, Loaded) end. load_plugin(Src) -> case compile:file(Src, [binary, return_errors]) of {ok, Mod, Bin} -> load_plugin_module(Mod, Bin, Src); {error, Errors, _Warnings} -> ?ABORT("Plugin ~s contains compilation errors: ~p~n", [Src, Errors]) end. load_plugin_module(Mod, Bin, Src) -> case code:is_loaded(Mod) of {file, Loaded} -> ?ABORT("Plugin ~p clashes with previously loaded module ~p~n", [Mod, Loaded]); false -> ?INFO("Loading plugin ~p from ~s~n", [Mod, Src]), {module, Mod} = code:load_binary(Mod, Src, Bin), Mod end. rebar-2.0.0/src/rebar_ct.erl000066400000000000000000000224711176314626300156770ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% %% Targets: %% test - runs common test suites in ./test %% int_test - runs suites in ./int_test %% perf_test - runs suites inm ./perf_test %% %% Global options: %% verbose=1 - show output from the common_test run as it goes %% suites="foo,bar" - runs /foo_SUITE and /bar_SUITE %% case="mycase" - runs individual test case foo_SUITE:mycase %% ------------------------------------------------------------------- -module(rebar_ct). -export([ct/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== ct(Config, File) -> TestDir = rebar_config:get_local(Config, ct_dir, "test"), run_test_if_present(TestDir, Config, File). %% =================================================================== %% Internal functions %% =================================================================== run_test_if_present(TestDir, Config, File) -> case filelib:is_dir(TestDir) of false -> ?WARN("~s directory not present - skipping\n", [TestDir]), ok; true -> run_test(TestDir, Config, File) end. run_test(TestDir, Config, _File) -> {Cmd, RawLog} = make_cmd(TestDir, Config), clear_log(RawLog), case rebar_config:is_verbose() of false -> Output = " >> " ++ RawLog ++ " 2>&1"; true -> Output = " 2>&1 | tee -a " ++ RawLog end, rebar_utils:sh(Cmd ++ Output, [{env,[{"TESTDIR", TestDir}]}]), check_log(RawLog). clear_log(RawLog) -> case filelib:ensure_dir("logs/index.html") of ok -> NowStr = rebar_utils:now_str(), LogHeader = "--- Test run on " ++ NowStr ++ " ---\n", ok = file:write_file(RawLog, LogHeader); {error, Reason} -> ?ERROR("Could not create log dir - ~p\n", [Reason]), ?FAIL end. %% calling ct with erl does not return non-zero on failure - have to check %% log results check_log(RawLog) -> {ok, Msg} = rebar_utils:sh("grep -e 'TEST COMPLETE' -e '{error,make_failed}' " ++ RawLog, [{use_stdout, false}]), MakeFailed = string:str(Msg, "{error,make_failed}") =/= 0, RunFailed = string:str(Msg, ", 0 failed") =:= 0, if MakeFailed -> show_log(RawLog), ?ERROR("Building tests failed\n",[]), ?FAIL; RunFailed -> show_log(RawLog), ?ERROR("One or more tests failed\n",[]), ?FAIL; true -> ?CONSOLE("DONE.\n~s\n", [Msg]) end. %% Show the log if it hasn't already been shown because verbose was on show_log(RawLog) -> ?CONSOLE("Showing log\n", []), case rebar_config:is_verbose() of false -> {ok, Contents} = file:read_file(RawLog), ?CONSOLE("~s", [Contents]); true -> ok end. make_cmd(TestDir, Config) -> Cwd = rebar_utils:get_cwd(), LogDir = filename:join(Cwd, "logs"), EbinDir = filename:absname(filename:join(Cwd, "ebin")), IncludeDir = filename:join(Cwd, "include"), Include = case filelib:is_dir(IncludeDir) of true -> " -include \"" ++ IncludeDir ++ "\""; false -> "" end, %% Add the code path of the rebar process to the code path. This %% includes the dependencies in the code path. The directories %% that are part of the root Erlang install are filtered out to %% avoid duplication R = code:root_dir(), NonLibCodeDirs = [P || P <- code:get_path(), not lists:prefix(R, P)], CodeDirs = [io_lib:format("\"~s\"", [Dir]) || Dir <- [EbinDir|NonLibCodeDirs]], CodePathString = string:join(CodeDirs, " "), Cmd = case get_ct_specs(Cwd) of undefined -> ?FMT("erl " % should we expand ERL_PATH? " -noshell -pa ~s ~s" " -name test@~s" " -logdir \"~s\"" " -env TEST_DIR \"~s\"" " ~s" " -s ct_run script_start -s erlang halt", [CodePathString, Include, net_adm:localhost(), LogDir, filename:join(Cwd, TestDir), get_extra_params(Config)]) ++ get_cover_config(Config, Cwd) ++ get_ct_config_file(TestDir) ++ get_config_file(TestDir) ++ get_suite(TestDir) ++ get_case(); SpecFlags -> ?FMT("erl " % should we expand ERL_PATH? " -noshell -pa ~s ~s" " -name test@~s" " -logdir \"~s\"" " -env TEST_DIR \"~s\"" " ~s" " -s ct_run script_start -s erlang halt", [CodePathString, Include, net_adm:localhost(), LogDir, filename:join(Cwd, TestDir), get_extra_params(Config)]) ++ SpecFlags ++ get_cover_config(Config, Cwd) end, RawLog = filename:join(LogDir, "raw.log"), {Cmd, RawLog}. get_extra_params(Config) -> rebar_config:get_local(Config, ct_extra_params, ""). get_ct_specs(Cwd) -> case collect_glob(Cwd, ".*\.test\.spec\$") of [] -> undefined; [Spec] -> " -spec " ++ Spec; Specs -> " -spec " ++ lists:flatten([io_lib:format("~s ", [Spec]) || Spec <- Specs]) end. get_cover_config(Config, Cwd) -> case rebar_config:get_local(Config, cover_enabled, false) of false -> ""; true -> case collect_glob(Cwd, ".*cover\.spec\$") of [] -> ?DEBUG("No cover spec found: ~s~n", [Cwd]), ""; [Spec] -> ?DEBUG("Found cover file ~w~n", [Spec]), " -cover " ++ Spec; Specs -> ?ABORT("Multiple cover specs found: ~p~n", [Specs]) end end. collect_glob(Cwd, Glob) -> filelib:fold_files(Cwd, Glob, true, fun collect_files/2, []). collect_files(F, Acc) -> %% Ignore any specs under the deps/ directory. Do this pulling %% the dirname off the the F and then splitting it into a list. Parts = filename:split(filename:dirname(F)), case lists:member("deps", Parts) of true -> Acc; % There is a directory named "deps" in path false -> [F | Acc] % No "deps" directory in path end. get_ct_config_file(TestDir) -> Config = filename:join(TestDir, "test.config"), case filelib:is_regular(Config) of false -> " "; true -> " -ct_config " ++ Config end. get_config_file(TestDir) -> Config = filename:join(TestDir, "app.config"), case filelib:is_regular(Config) of false -> " "; true -> " -config " ++ Config end. get_suite(TestDir) -> case rebar_utils:get_deprecated_global(suite, suites, "soon") of undefined -> " -dir " ++ TestDir; Suites -> Suites1 = string:tokens(Suites, ","), Suites2 = [find_suite_path(Suite, TestDir) || Suite <- Suites1], string:join([" -suite"] ++ Suites2, " ") end. find_suite_path(Suite, TestDir) -> Path = filename:join(TestDir, Suite ++ "_SUITE.erl"), case filelib:is_regular(Path) of false -> ?ERROR("Suite ~s not found\n", [Suite]), ?FAIL; true -> Path end. get_case() -> case rebar_config:get_global('case', undefined) of undefined -> ""; Case -> " -case " ++ Case end. rebar-2.0.0/src/rebar_deps.erl000066400000000000000000000502021176314626300162150ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_deps). -include("rebar.hrl"). -export([preprocess/2, postprocess/2, compile/2, setup_env/1, 'check-deps'/2, 'get-deps'/2, 'update-deps'/2, 'delete-deps'/2, 'list-deps'/2]). -record(dep, { dir, app, vsn_regex, source }). %% =================================================================== %% Public API %% =================================================================== preprocess(Config, _) -> %% Side effect to set deps_dir globally for all dependencies from %% top level down. Means the root deps_dir is honoured or the default %% used globally since it will be set on the first time through here set_global_deps_dir(Config, rebar_config:get_global(deps_dir, [])), %% Get the list of deps for the current working directory and identify those %% deps that are available/present. Deps = rebar_config:get_local(Config, deps, []), {AvailableDeps, MissingDeps} = find_deps(find, Deps), ?DEBUG("Available deps: ~p\n", [AvailableDeps]), ?DEBUG("Missing deps : ~p\n", [MissingDeps]), %% Add available deps to code path update_deps_code_path(AvailableDeps), %% If skip_deps=true, mark each dep dir as a skip_dir w/ the core so that %% the current command doesn't run on the dep dir. However, pre/postprocess %% WILL run (and we want it to) for transitivity purposes. case rebar_config:get_global(skip_deps, false) of "true" -> lists:foreach(fun (#dep{dir = Dir}) -> rebar_core:skip_dir(Dir) end, AvailableDeps); _ -> ok end, %% Return all the available dep directories for process {ok, [D#dep.dir || D <- AvailableDeps]}. postprocess(_Config, _) -> case erlang:get(?MODULE) of undefined -> {ok, []}; Dirs -> erlang:erase(?MODULE), {ok, Dirs} end. compile(Config, AppFile) -> 'check-deps'(Config, AppFile). %% set REBAR_DEPS_DIR and ERL_LIBS environment variables setup_env(_Config) -> {true, DepsDir} = get_deps_dir(), %% include rebar's DepsDir in ERL_LIBS Separator = case os:type() of {win32, nt} -> ";"; _ -> ":" end, ERL_LIBS = case os:getenv("ERL_LIBS") of false -> {"ERL_LIBS", DepsDir}; PrevValue -> {"ERL_LIBS", DepsDir ++ Separator ++ PrevValue} end, [{"REBAR_DEPS_DIR", DepsDir}, ERL_LIBS]. 'check-deps'(Config, _) -> %% Get the list of immediate (i.e. non-transitive) deps that are missing Deps = rebar_config:get_local(Config, deps, []), case find_deps(find, Deps) of {_, []} -> %% No missing deps ok; {_, MissingDeps} -> lists:foreach(fun (#dep{app=App, vsn_regex=Vsn, source=Src}) -> ?CONSOLE("Dependency not available: " "~p-~s (~p)\n", [App, Vsn, Src]) end, MissingDeps), ?FAIL end. 'get-deps'(Config, _) -> %% Determine what deps are available and missing Deps = rebar_config:get_local(Config, deps, []), {_AvailableDeps, MissingDeps} = find_deps(find, Deps), %% For each missing dep with a specified source, try to pull it. PulledDeps = [use_source(D) || D <- MissingDeps, D#dep.source /= undefined], %% Add each pulled dep to our list of dirs for post-processing. This yields %% the necessary transitivity of the deps erlang:put(?MODULE, [D#dep.dir || D <- PulledDeps]), ok. 'update-deps'(Config, _) -> %% Determine what deps are available and missing Deps = rebar_config:get_local(Config, deps, []), UpdatedDeps = [update_source(D) || D <- find_deps(read, Deps), D#dep.source /= undefined], %% Add each updated dep to our list of dirs for post-processing. This yields %% the necessary transitivity of the deps erlang:put(?MODULE, [D#dep.dir || D <- UpdatedDeps]), ok. 'delete-deps'(Config, _) -> %% Delete all the available deps in our deps/ directory, if any {true, DepsDir} = get_deps_dir(), Deps = rebar_config:get_local(Config, deps, []), {AvailableDeps, _} = find_deps(find, Deps), _ = [delete_dep(D) || D <- AvailableDeps, lists:prefix(DepsDir, D#dep.dir)], ok. 'list-deps'(Config, _) -> Deps = rebar_config:get_local(Config, deps, []), case find_deps(find, Deps) of {AvailDeps, []} -> lists:foreach(fun(Dep) -> print_source(Dep) end, AvailDeps), ok; {_, MissingDeps} -> ?ABORT("Missing dependencies: ~p\n", [MissingDeps]) end. %% =================================================================== %% Internal functions %% =================================================================== %% Added because of trans deps, %% need all deps in same dir and should be the one set by the root rebar.config %% Sets a default if root config has no deps_dir set set_global_deps_dir(Config, []) -> rebar_config:set_global(deps_dir, rebar_config:get_local(Config, deps_dir, "deps")); set_global_deps_dir(_Config, _DepsDir) -> ok. get_deps_dir() -> get_deps_dir(""). get_deps_dir(App) -> BaseDir = rebar_config:get_global(base_dir, []), DepsDir = rebar_config:get_global(deps_dir, "deps"), {true, filename:join([BaseDir, DepsDir, App])}. get_lib_dir(App) -> %% Find App amongst the reachable lib directories %% Returns either the found path or a tagged tuple with a boolean %% to match get_deps_dir's return type case code:lib_dir(App) of {error, bad_name} -> {false, bad_name}; Path -> {true, Path} end. update_deps_code_path([]) -> ok; update_deps_code_path([Dep | Rest]) -> case is_app_available(Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir) of {true, _} -> Dir = filename:join(Dep#dep.dir, "ebin"), ok = filelib:ensure_dir(filename:join(Dir, "dummy")), ?DEBUG("Adding ~s to code path~n", [Dir]), true = code:add_patha(Dir); {false, _} -> true end, update_deps_code_path(Rest). find_deps(find=Mode, Deps) -> find_deps(Mode, Deps, {[], []}); find_deps(read=Mode, Deps) -> find_deps(Mode, Deps, []). find_deps(find, [], {Avail, Missing}) -> {lists:reverse(Avail), lists:reverse(Missing)}; find_deps(read, [], Deps) -> lists:reverse(Deps); find_deps(Mode, [App | Rest], Acc) when is_atom(App) -> find_deps(Mode, [{App, ".*", undefined} | Rest], Acc); find_deps(Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) -> find_deps(Mode, [{App, VsnRegex, undefined} | Rest], Acc); find_deps(Mode, [{App, VsnRegex, Source} | Rest], Acc) -> Dep = #dep { app = App, vsn_regex = VsnRegex, source = Source }, {Availability, FoundDir} = find_dep(Dep), find_deps(Mode, Rest, acc_deps(Mode, Availability, Dep, FoundDir, Acc)); find_deps(_Mode, [Other | _Rest], _Acc) -> ?ABORT("Invalid dependency specification ~p in ~s\n", [Other, rebar_utils:get_cwd()]). find_dep(Dep) -> %% Find a dep based on its source, %% e.g. {git, "https://github.com/mochi/mochiweb.git", "HEAD"} %% Deps with a source must be found (or fetched) locally. %% Those without a source may be satisfied from lib dir (get_lib_dir). find_dep(Dep, Dep#dep.source). find_dep(Dep, undefined) -> %% 'source' is undefined. If Dep is not satisfied locally, %% go ahead and find it amongst the lib_dir's. case find_dep_in_dir(Dep, get_deps_dir(Dep#dep.app)) of {avail, _Dir} = Avail -> Avail; {missing, _} -> find_dep_in_dir(Dep, get_lib_dir(Dep#dep.app)) end; find_dep(Dep, _Source) -> %% _Source is defined. Regardless of what it is, we must find it %% locally satisfied or fetch it from the original source %% into the project's deps find_dep_in_dir(Dep, get_deps_dir(Dep#dep.app)). find_dep_in_dir(_Dep, {false, Dir}) -> {missing, Dir}; find_dep_in_dir(Dep, {true, Dir}) -> App = Dep#dep.app, VsnRegex = Dep#dep.vsn_regex, case is_app_available(App, VsnRegex, Dir) of {true, _AppFile} -> {avail, Dir}; {false, _} -> {missing, Dir} end. acc_deps(find, avail, Dep, AppDir, {Avail, Missing}) -> {[Dep#dep { dir = AppDir } | Avail], Missing}; acc_deps(find, missing, Dep, AppDir, {Avail, Missing}) -> {Avail, [Dep#dep { dir = AppDir } | Missing]}; acc_deps(read, _, Dep, AppDir, Acc) -> [Dep#dep { dir = AppDir } | Acc]. delete_dep(D) -> case filelib:is_dir(D#dep.dir) of true -> ?INFO("Deleting dependency: ~s\n", [D#dep.dir]), rebar_file_utils:rm_rf(D#dep.dir); false -> ok end. require_source_engine(Source) -> true = source_engine_avail(Source), ok. is_app_available(App, VsnRegex, Path) -> ?DEBUG("is_app_available, looking for App ~p with Path ~p~n", [App, Path]), case rebar_app_utils:is_app_dir(Path) of {true, AppFile} -> case rebar_app_utils:app_name(AppFile) of App -> Vsn = rebar_app_utils:app_vsn(AppFile), ?INFO("Looking for ~s-~s ; found ~s-~s at ~s\n", [App, VsnRegex, App, Vsn, Path]), case re:run(Vsn, VsnRegex, [{capture, none}]) of match -> {true, Path}; nomatch -> ?WARN("~s has version ~p; requested regex was ~s\n", [AppFile, Vsn, VsnRegex]), {false, {version_mismatch, {AppFile, {expected, VsnRegex}, {has, Vsn}}}} end; OtherApp -> ?WARN("~s has application id ~p; expected ~p\n", [AppFile, OtherApp, App]), {false, {name_mismatch, {AppFile, {expected, App}, {has, OtherApp}}}} end; false -> ?WARN("Expected ~s to be an app dir (containing ebin/*.app), " "but no .app found.\n", [Path]), {false, {missing_app_file, Path}} end. use_source(Dep) -> use_source(Dep, 3). use_source(Dep, 0) -> ?ABORT("Failed to acquire source from ~p after 3 tries.\n", [Dep#dep.source]); use_source(Dep, Count) -> case filelib:is_dir(Dep#dep.dir) of true -> %% Already downloaded -- verify the versioning matches the regex case is_app_available(Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir) of {true, _} -> Dir = filename:join(Dep#dep.dir, "ebin"), ok = filelib:ensure_dir(filename:join(Dir, "dummy")), %% Available version matches up -- we're good to go; %% add the app dir to our code path true = code:add_patha(Dir), Dep; {false, Reason} -> %% The app that was downloaded doesn't match up (or had %% errors or something). For the time being, abort. ?ABORT("Dependency dir ~s failed application validation " "with reason:~n~p.\n", [Dep#dep.dir, Reason]) end; false -> ?CONSOLE("Pulling ~p from ~p\n", [Dep#dep.app, Dep#dep.source]), require_source_engine(Dep#dep.source), {true, TargetDir} = get_deps_dir(Dep#dep.app), download_source(TargetDir, Dep#dep.source), use_source(Dep#dep { dir = TargetDir }, Count-1) end. download_source(AppDir, {hg, Url, Rev}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]), [{cd, filename:dirname(AppDir)}]), rebar_utils:sh(?FMT("hg update ~s", [Rev]), [{cd, AppDir}]); download_source(AppDir, {git, Url}) -> download_source(AppDir, {git, Url, {branch, "HEAD"}}); download_source(AppDir, {git, Url, ""}) -> download_source(AppDir, {git, Url, {branch, "HEAD"}}); download_source(AppDir, {git, Url, {branch, Branch}}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]), [{cd, filename:dirname(AppDir)}]), rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [{cd, AppDir}]); download_source(AppDir, {git, Url, {tag, Tag}}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]), [{cd, filename:dirname(AppDir)}]), rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [{cd, AppDir}]); download_source(AppDir, {git, Url, Rev}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]), [{cd, filename:dirname(AppDir)}]), rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, AppDir}]); download_source(AppDir, {bzr, Url, Rev}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("bzr branch -r ~s ~s ~s", [Rev, Url, filename:basename(AppDir)]), [{cd, filename:dirname(AppDir)}]); download_source(AppDir, {svn, Url, Rev}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("svn checkout -r ~s ~s ~s", [Rev, Url, filename:basename(AppDir)]), [{cd, filename:dirname(AppDir)}]); download_source(AppDir, {rsync, Url}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s", [Url, AppDir]), []). update_source(Dep) -> %% It's possible when updating a source, that a given dep does not have a %% VCS directory, such as when a source archive is built of a project, with %% all deps already downloaded/included. So, verify that the necessary VCS %% directory exists before attempting to do the update. {true, AppDir} = get_deps_dir(Dep#dep.app), case has_vcs_dir(element(1, Dep#dep.source), AppDir) of true -> ?CONSOLE("Updating ~p from ~p\n", [Dep#dep.app, Dep#dep.source]), require_source_engine(Dep#dep.source), update_source(AppDir, Dep#dep.source), Dep; false -> ?WARN("Skipping update for ~p: " "no VCS directory available!\n", [Dep]), Dep end. update_source(AppDir, {git, Url}) -> update_source(AppDir, {git, Url, {branch, "HEAD"}}); update_source(AppDir, {git, Url, ""}) -> update_source(AppDir, {git, Url, {branch, "HEAD"}}); update_source(AppDir, {git, _Url, {branch, Branch}}) -> ShOpts = [{cd, AppDir}], rebar_utils:sh("git fetch origin", ShOpts), rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), ShOpts); update_source(AppDir, {git, _Url, {tag, Tag}}) -> ShOpts = [{cd, AppDir}], rebar_utils:sh("git fetch --tags origin", ShOpts), rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), ShOpts); update_source(AppDir, {git, _Url, Refspec}) -> ShOpts = [{cd, AppDir}], rebar_utils:sh("git fetch origin", ShOpts), rebar_utils:sh(?FMT("git checkout -q ~s", [Refspec]), ShOpts); update_source(AppDir, {svn, _Url, Rev}) -> rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [{cd, AppDir}]); update_source(AppDir, {hg, _Url, Rev}) -> rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [{cd, AppDir}]); update_source(AppDir, {bzr, _Url, Rev}) -> rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [{cd, AppDir}]); update_source(AppDir, {rsync, Url}) -> rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]). %% =================================================================== %% Source helper functions %% =================================================================== source_engine_avail(Source) -> Name = element(1, Source), source_engine_avail(Name, Source). source_engine_avail(Name, Source) when Name == hg; Name == git; Name == svn; Name == bzr; Name == rsync -> case vcs_client_vsn(Name) >= required_vcs_client_vsn(Name) of true -> true; false -> ?ABORT("Rebar requires version ~p or higher of ~s to process ~p\n", [required_vcs_client_vsn(Name), Name, Source]) end. vcs_client_vsn(false, _VsnArg, _VsnRegex) -> false; vcs_client_vsn(Path, VsnArg, VsnRegex) -> {ok, Info} = rebar_utils:sh(Path ++ VsnArg, [{env, [{"LANG", "C"}]}, {use_stdout, false}]), case re:run(Info, VsnRegex, [{capture, all_but_first, list}]) of {match, Match} -> list_to_tuple([list_to_integer(S) || S <- Match]); _ -> false end. required_vcs_client_vsn(hg) -> {1, 1}; required_vcs_client_vsn(git) -> {1, 5}; required_vcs_client_vsn(bzr) -> {2, 0}; required_vcs_client_vsn(svn) -> {1, 6}; required_vcs_client_vsn(rsync) -> {2, 0}. vcs_client_vsn(hg) -> vcs_client_vsn(rebar_utils:find_executable("hg"), " --version", "version (\\d+).(\\d+)"); vcs_client_vsn(git) -> vcs_client_vsn(rebar_utils:find_executable("git"), " --version", "git version (\\d+).(\\d+)"); vcs_client_vsn(bzr) -> vcs_client_vsn(rebar_utils:find_executable("bzr"), " --version", "Bazaar \\(bzr\\) (\\d+).(\\d+)"); vcs_client_vsn(svn) -> vcs_client_vsn(rebar_utils:find_executable("svn"), " --version", "svn, version (\\d+).(\\d+)"); vcs_client_vsn(rsync) -> vcs_client_vsn(rebar_utils:find_executable("rsync"), " --version", "rsync version (\\d+).(\\d+)"). has_vcs_dir(git, Dir) -> filelib:is_dir(filename:join(Dir, ".git")); has_vcs_dir(hg, Dir) -> filelib:is_dir(filename:join(Dir, ".hg")); has_vcs_dir(bzr, Dir) -> filelib:is_dir(filename:join(Dir, ".bzr")); has_vcs_dir(svn, Dir) -> filelib:is_dir(filename:join(Dir, ".svn")) orelse filelib:is_dir(filename:join(Dir, "_svn")); has_vcs_dir(rsync, _) -> true; has_vcs_dir(_, _) -> true. print_source(#dep{app=App, source=Source}) -> ?CONSOLE("~s~n", [format_source(App, Source)]). format_source(App, {git, Url}) -> ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]); format_source(App, {git, Url, ""}) -> ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]); format_source(App, {git, Url, {branch, Branch}}) -> ?FMT("~p BRANCH ~s ~s", [App, Branch, Url]); format_source(App, {git, Url, {tag, Tag}}) -> ?FMT("~p TAG ~s ~s", [App, Tag, Url]); format_source(App, {_, Url, Rev}) -> ?FMT("~p REV ~s ~s", [App, Rev, Url]); format_source(App, undefined) -> ?FMT("~p", [App]). rebar-2.0.0/src/rebar_edoc.erl000066400000000000000000000056341176314626300162050ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% @author Dave Smith %% @doc rebar_edoc supports the following command: %%
    %%
  • doc (essentially erl -noshell -run edoc_run application %% "'$(<app_name>)'" %% '"."' '[<options>]')
  • %%
%% EDoc options can be given in the edoc_opts option in %% rebar.config. %% @copyright 2010 Dave Smith %% ------------------------------------------------------------------- -module(rebar_edoc). -export([doc/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== %% @doc Generate Erlang program documentation. -spec doc(Config::rebar_config:config(), File::file:filename()) -> ok. doc(Config, File) -> %% Save code path CodePath = setup_code_path(), {ok, AppName, _AppData} = rebar_app_utils:load_app_file(File), EDocOpts = rebar_config:get(Config, edoc_opts, []), ok = edoc:application(AppName, ".", EDocOpts), %% Restore code path true = code:set_path(CodePath), ok. %% =================================================================== %% Internal functions %% =================================================================== setup_code_path() -> %% Setup code path prior to calling edoc so that edown, asciiedoc, %% and the like can work properly when generating their own %% documentation. CodePath = code:get_path(), true = code:add_patha(ebin_dir()), CodePath. ebin_dir() -> filename:join(rebar_utils:get_cwd(), "ebin"). rebar-2.0.0/src/rebar_erlc_compiler.erl000066400000000000000000000410261176314626300201050ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_erlc_compiler). -export([compile/2, clean/2]). -export([doterl_compile/2, doterl_compile/3]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== %% Supported configuration variables: %% %% * erl_opts - Erlang list of options passed to compile:file/2 %% It is also possible to specify platform specific %% options by specifying a pair or a triplet where the %% first string is a regex that is checked against the %% string %% %% OtpRelease ++ "-" ++ SysArch ++ "-" ++ Words. %% %% where %% %% OtpRelease = erlang:system_info(otp_release). %% SysArch = erlang:system_info(system_architecture). %% Words = integer_to_list(8 * %% erlang:system_info({wordsize, external})). %% %% E.g. to define HAVE_SENDFILE only on systems with %% sendfile(), to define BACKLOG on Linux/FreeBSD as 128, %% and to define 'old_inets' for R13 OTP release do: %% %% {erl_opts, [{platform_define, %% "(linux|solaris|freebsd|darwin)", %% 'HAVE_SENDFILE'}, %% {platform_define, "(linux|freebsd)", %% 'BACKLOG', 128}, %% {platform_define, "R13", %% 'old_inets'}]}. %% -spec compile(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. compile(Config, _AppFile) -> rebar_base_compiler:run(Config, check_files(rebar_config:get_local( Config, xrl_first_files, [])), "src", ".xrl", "src", ".erl", fun compile_xrl/3), rebar_base_compiler:run(Config, check_files(rebar_config:get_local( Config, yrl_first_files, [])), "src", ".yrl", "src", ".erl", fun compile_yrl/3), rebar_base_compiler:run(Config, check_files(rebar_config:get_local( Config, mib_first_files, [])), "mibs", ".mib", "priv/mibs", ".bin", fun compile_mib/3), doterl_compile(Config, "ebin"). -spec clean(Config::rebar_config:config(), AppFile::file:filename()) -> 'ok'. clean(_Config, _AppFile) -> MibFiles = rebar_utils:find_files("mibs", "^.*\\.mib\$"), MIBs = [filename:rootname(filename:basename(MIB)) || MIB <- MibFiles], rebar_file_utils:delete_each( [filename:join(["include",MIB++".hrl"]) || MIB <- MIBs]), lists:foreach(fun(F) -> ok = rebar_file_utils:rm_rf(F) end, ["ebin/*.beam", "priv/mibs/*.bin"]), YrlFiles = rebar_utils:find_files("src", "^.*\\.[x|y]rl\$"), rebar_file_utils:delete_each( [ binary_to_list(iolist_to_binary(re:replace(F, "\\.[x|y]rl$", ".erl"))) || F <- YrlFiles ]), %% Erlang compilation is recursive, so it's possible that we have a nested %% directory structure in ebin with .beam files within. As such, we want %% to scan whatever is left in the ebin/ directory for sub-dirs which %% satisfy our criteria. BeamFiles = rebar_utils:find_files("ebin", "^.*\\.beam\$"), rebar_file_utils:delete_each(BeamFiles), lists:foreach(fun(Dir) -> delete_dir(Dir, dirs(Dir)) end, dirs("ebin")), ok. %% =================================================================== %% .erl Compilation API (externally used by only eunit) %% =================================================================== -spec doterl_compile(Config::rebar_config:config(), OutDir::file:filename()) -> 'ok'. doterl_compile(Config, OutDir) -> doterl_compile(Config, OutDir, []). doterl_compile(Config, OutDir, MoreSources) -> FirstErls = rebar_config:get_list(Config, erl_first_files, []), ErlOpts = erl_opts(Config), ?DEBUG("erl_opts ~p~n", [ErlOpts]), %% Support the src_dirs option allowing multiple directories to %% contain erlang source. This might be used, for example, should %% eunit tests be separated from the core application source. SrcDirs = src_dirs(proplists:append_values(src_dirs, ErlOpts)), RestErls = [Source || Source <- gather_src(SrcDirs, []) ++ MoreSources, not lists:member(Source, FirstErls)], %% Split RestErls so that parse_transforms and behaviours are instead added %% to erl_first_files, parse transforms first. %% This should probably be somewhat combined with inspect_epp [ParseTransforms, Behaviours, OtherErls] = lists:foldl(fun(F, [A, B, C]) -> case compile_priority(F) of parse_transform -> [[F | A], B, C]; behaviour -> [A, [F | B], C]; callback -> [A, [F | B], C]; _ -> [A, B, [F | C]] end end, [[], [], []], RestErls), NewFirstErls = FirstErls ++ ParseTransforms ++ Behaviours, %% Make sure that ebin/ exists and is on the path ok = filelib:ensure_dir(filename:join("ebin", "dummy.beam")), CurrPath = code:get_path(), true = code:add_path(filename:absname("ebin")), rebar_base_compiler:run(Config, NewFirstErls, OtherErls, fun(S, C) -> internal_erl_compile(S, C, OutDir, ErlOpts) end), true = code:set_path(CurrPath), ok. %% =================================================================== %% Internal functions %% =================================================================== erl_opts(Config) -> RawErlOpts = filter_defines(rebar_config:get(Config, erl_opts, []), []), GlobalDefines = [{d, list_to_atom(D)} || D <- rebar_config:get_global(defines, [])], Opts = GlobalDefines ++ RawErlOpts, case proplists:is_defined(no_debug_info, Opts) of true -> [O || O <- Opts, O =/= no_debug_info]; false -> [debug_info|Opts] end. -spec include_path(Source::file:filename(), Config::rebar_config:config()) -> [file:filename(), ...]. include_path(Source, Config) -> ErlOpts = rebar_config:get(Config, erl_opts, []), ["include", filename:dirname(Source)] ++ proplists:get_all_values(i, ErlOpts). -spec inspect(Source::file:filename(), IncludePath::[file:filename(), ...]) -> {string(), [string()]}. inspect(Source, IncludePath) -> ModuleDefault = filename:basename(Source, ".erl"), case epp:open(Source, IncludePath) of {ok, Epp} -> inspect_epp(Epp, Source, ModuleDefault, []); {error, Reason} -> ?DEBUG("Failed to inspect ~s: ~p\n", [Source, Reason]), {ModuleDefault, []} end. -spec inspect_epp(Epp::pid(), Source::file:filename(), Module::file:filename(), Includes::[string()]) -> {string(), [string()]}. inspect_epp(Epp, Source, Module, Includes) -> case epp:parse_erl_form(Epp) of {ok, {attribute, _, module, ModInfo}} -> case ModInfo of %% Typical module name, single atom ActualModule when is_atom(ActualModule) -> ActualModuleStr = atom_to_list(ActualModule); %% Packag-ized module name, list of atoms ActualModule when is_list(ActualModule) -> ActualModuleStr = string:join([atom_to_list(P) || P <- ActualModule], "."); %% Parameterized module name, single atom {ActualModule, _} when is_atom(ActualModule) -> ActualModuleStr = atom_to_list(ActualModule); %% Parameterized and packagized module name, list of atoms {ActualModule, _} when is_list(ActualModule) -> ActualModuleStr = string:join([atom_to_list(P) || P <- ActualModule], ".") end, inspect_epp(Epp, Source, ActualModuleStr, Includes); {ok, {attribute, 1, file, {Module, 1}}} -> inspect_epp(Epp, Source, Module, Includes); {ok, {attribute, 1, file, {Source, 1}}} -> inspect_epp(Epp, Source, Module, Includes); {ok, {attribute, 1, file, {IncFile, 1}}} -> inspect_epp(Epp, Source, Module, [IncFile | Includes]); {eof, _} -> epp:close(Epp), {Module, Includes}; _ -> inspect_epp(Epp, Source, Module, Includes) end. -spec needs_compile(Source::file:filename(), Target::file:filename(), Hrls::[string()]) -> boolean(). needs_compile(Source, Target, Hrls) -> TargetLastMod = filelib:last_modified(Target), lists:any(fun(I) -> TargetLastMod < filelib:last_modified(I) end, [Source] ++ Hrls). -spec internal_erl_compile(Source::file:filename(), Config::rebar_config:config(), Outdir::file:filename(), ErlOpts::list()) -> 'ok' | 'skipped'. internal_erl_compile(Source, Config, Outdir, ErlOpts) -> %% Determine the target name and includes list by inspecting the source file {Module, Hrls} = inspect(Source, include_path(Source, Config)), %% Construct the target filename Target = filename:join([Outdir | string:tokens(Module, ".")]) ++ ".beam", ok = filelib:ensure_dir(Target), %% If the file needs compilation, based on last mod date of includes or %% the target case needs_compile(Source, Target, Hrls) of true -> Opts = [{outdir, filename:dirname(Target)}] ++ ErlOpts ++ [{i, "include"}, report], case compile:file(Source, Opts) of {ok, _} -> ok; _ -> ?FAIL end; false -> skipped end. -spec compile_mib(Source::file:filename(), Target::file:filename(), Config::rebar_config:config()) -> 'ok'. compile_mib(Source, Target, Config) -> ok = rebar_utils:ensure_dir(Target), ok = rebar_utils:ensure_dir(filename:join("include", "dummy.hrl")), Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++ rebar_config:get(Config, mib_opts, []), case snmpc:compile(Source, Opts) of {ok, _} -> Mib = filename:rootname(Target), ok = snmpc:mib_to_hrl(Mib), Hrl_filename = Mib ++ ".hrl", rebar_file_utils:mv(Hrl_filename, "include"), ok; {error, compilation_failed} -> ?FAIL end. -spec compile_xrl(Source::file:filename(), Target::file:filename(), Config::rebar_config:config()) -> 'ok'. compile_xrl(Source, Target, Config) -> Opts = [{scannerfile, Target} | rebar_config:get(Config, xrl_opts, [])], compile_xrl_yrl(Source, Target, Opts, leex). -spec compile_yrl(Source::file:filename(), Target::file:filename(), Config::rebar_config:config()) -> 'ok'. compile_yrl(Source, Target, Config) -> Opts = [{parserfile, Target} | rebar_config:get(Config, yrl_opts, [])], compile_xrl_yrl(Source, Target, Opts, yecc). -spec compile_xrl_yrl(Source::file:filename(), Target::file:filename(), Opts::list(), Mod::atom()) -> 'ok'. compile_xrl_yrl(Source, Target, Opts, Mod) -> case needs_compile(Source, Target, []) of true -> case Mod:file(Source, Opts) of {ok, _} -> ok; _X -> ?FAIL end; false -> skipped end. gather_src([], Srcs) -> Srcs; gather_src([Dir|Rest], Srcs) -> gather_src(Rest, Srcs ++ rebar_utils:find_files(Dir, ".*\\.erl\$")). -spec src_dirs(SrcDirs::[string()]) -> [file:filename(), ...]. src_dirs([]) -> ["src"]; src_dirs(SrcDirs) -> SrcDirs. -spec dirs(Dir::file:filename()) -> [file:filename()]. dirs(Dir) -> [F || F <- filelib:wildcard(filename:join([Dir, "*"])), filelib:is_dir(F)]. -spec delete_dir(Dir::file:filename(), Subdirs::[string()]) -> 'ok' | {'error', atom()}. delete_dir(Dir, []) -> file:del_dir(Dir); delete_dir(Dir, Subdirs) -> lists:foreach(fun(D) -> delete_dir(D, dirs(D)) end, Subdirs), file:del_dir(Dir). -spec compile_priority(File::file:filename()) -> 'normal' | 'behaviour' | 'callback' | 'parse_transform'. compile_priority(File) -> case epp_dodger:parse_file(File) of {error, _} -> normal; % couldn't parse the file, default priority {ok, Trees} -> F2 = fun({tree,arity_qualifier,_, {arity_qualifier,{tree,atom,_,behaviour_info}, {tree,integer,_,1}}}, _) -> behaviour; ({tree,arity_qualifier,_, {arity_qualifier,{tree,atom,_,parse_transform}, {tree,integer,_,2}}}, _) -> parse_transform; (_, Acc) -> Acc end, F = fun({tree, attribute, _, {attribute, {tree, atom, _, export}, [{tree, list, _, {list, List, none}}]}}, Acc) -> lists:foldl(F2, Acc, List); ({tree, attribute, _, {attribute, {tree, atom, _, callback},_}}, _Acc) -> callback; (_, Acc) -> Acc end, lists:foldl(F, normal, Trees) end. %% %% Filter a list of erl_opts platform_define options such that only %% those which match the provided architecture regex are returned. %% -spec filter_defines(ErlOpts::list(), Acc::list()) -> list(). filter_defines([], Acc) -> lists:reverse(Acc); filter_defines([{platform_define, ArchRegex, Key} | Rest], Acc) -> case rebar_utils:is_arch(ArchRegex) of true -> filter_defines(Rest, [{d, Key} | Acc]); false -> filter_defines(Rest, Acc) end; filter_defines([{platform_define, ArchRegex, Key, Value} | Rest], Acc) -> case rebar_utils:is_arch(ArchRegex) of true -> filter_defines(Rest, [{d, Key, Value} | Acc]); false -> filter_defines(Rest, Acc) end; filter_defines([Opt | Rest], Acc) -> filter_defines(Rest, [Opt | Acc]). %% %% Ensure all files in a list are present and abort if one is missing %% -spec check_files(FileList::[file:filename()]) -> [file:filename()]. check_files(FileList) -> [check_file(F) || F <- FileList]. check_file(File) -> case filelib:is_regular(File) of false -> ?ABORT("File ~p is missing, aborting\n", [File]); true -> File end. rebar-2.0.0/src/rebar_erlydtl_compiler.erl000066400000000000000000000160431176314626300206400ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com), %% Bryan Fink (bryan@basho.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% The rebar_erlydtl_compiler module is a plugin for rebar that compiles %% ErlyDTL templates. By default, it compiles all templates/*.dtl %% to ebin/*_dtl.beam. %% %% Configuration options should be placed in rebar.config under %% 'erlydtl_opts'. Available options include: %% %% doc_root: where to find templates to compile %% "templates" by default %% %% out_dir: where to put compiled template beam files %% "ebin" by default %% %% source_ext: the file extension the template sources have %% ".dtl" by default %% %% module_ext: characters to append to the template's module name %% "_dtl" by default %% %% For example, if you had: %% /t_src/ %% base.html %% foo.html %% %% And you wanted them compiled to: %% /priv/ %% base.beam %% foo.beam %% %% You would add to your rebar.config: %% {erlydtl_opts, [ %% {doc_root, "t_src"}, %% {out_dir, "priv"}, %% {source_ext, ".html"}, %% {module_ext, ""} %% ]}. %% %% The default settings are the equivalent of: %% {erlydtl_opts, [ %% {doc_root, "templates"}, %% {out_dir, "ebin"}, %% {source_ext, ".dtl"}, %% {module_ext, "_dtl"} %% ]}. -module(rebar_erlydtl_compiler). -export([compile/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== compile(Config, _AppFile) -> DtlOpts = erlydtl_opts(Config), OrigPath = code:get_path(), true = code:add_path(filename:join(rebar_utils:get_cwd(), "ebin")), Result = rebar_base_compiler:run(Config, [], option(doc_root, DtlOpts), option(source_ext, DtlOpts), option(out_dir, DtlOpts), option(module_ext, DtlOpts) ++ ".beam", fun compile_dtl/3, [{check_last_mod, false}]), true = code:set_path(OrigPath), Result. %% =================================================================== %% Internal functions %% =================================================================== erlydtl_opts(Config) -> rebar_config:get(Config, erlydtl_opts, []). option(Opt, DtlOpts) -> proplists:get_value(Opt, DtlOpts, default(Opt)). default(doc_root) -> "templates"; default(out_dir) -> "ebin"; default(source_ext) -> ".dtl"; default(module_ext) -> "_dtl"; default(custom_tags_dir) -> "". compile_dtl(Source, Target, Config) -> case code:which(erlydtl) of non_existing -> ?ERROR("~n===============================================~n" " You need to install erlydtl to compile DTL templates~n" " Download the latest tarball release from github~n" " http://code.google.com/p/erlydtl/~n" " and install it into your erlang library dir~n" "===============================================~n~n", []), ?FAIL; _ -> case needs_compile(Source, Target, Config) of true -> do_compile(Source, Target, Config); false -> skipped end end. do_compile(Source, Target, Config) -> %% TODO: Check last mod on target and referenced DTLs here.. DtlOpts = erlydtl_opts(Config), %% ensure that doc_root and out_dir are defined, %% using defaults if necessary Opts = [{out_dir, option(out_dir, DtlOpts)}, {doc_root, option(doc_root, DtlOpts)}, {custom_tags_dir, option(custom_tags_dir, DtlOpts)}, report, return], case erlydtl:compile(Source, module_name(Target), Opts++DtlOpts) of ok -> ok; Reason -> ?ERROR("Compiling template ~s failed:~n ~p~n", [Source, Reason]), ?FAIL end. module_name(Target) -> F = filename:basename(Target), string:substr(F, 1, length(F)-length(".beam")). needs_compile(Source, Target, Config) -> LM = filelib:last_modified(Target), LM < filelib:last_modified(Source) orelse lists:any(fun(D) -> LM < filelib:last_modified(D) end, referenced_dtls(Source, Config)). referenced_dtls(Source, Config) -> Set = referenced_dtls1([Source], Config, sets:add_element(Source, sets:new())), sets:to_list(sets:del_element(Source, Set)). referenced_dtls1(Step, Config, Seen) -> DtlOpts = erlydtl_opts(Config), ExtMatch = re:replace(option(source_ext, DtlOpts), "\.", "\\\\\\\\.", [{return, list}]), ShOpts = [{use_stdout, false}, return_on_error], AllRefs = lists:append( [begin Cmd = lists:flatten(["grep -o [^\\\"]*", ExtMatch, " ", F]), case rebar_utils:sh(Cmd, ShOpts) of {ok, Res} -> string:tokens(Res, "\n"); {error, _} -> "" end end || F <- Step]), DocRoot = option(doc_root, DtlOpts), WithPaths = [ filename:join([DocRoot, F]) || F <- AllRefs ], Existing = [F || F <- WithPaths, filelib:is_regular(F)], New = sets:subtract(sets:from_list(Existing), Seen), case sets:size(New) of 0 -> Seen; _ -> referenced_dtls1(sets:to_list(New), Config, sets:union(New, Seen)) end. rebar-2.0.0/src/rebar_escripter.erl000066400000000000000000000116651176314626300172740ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_escripter). -export([escriptize/2, clean/2]). -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). %% =================================================================== %% Public API %% =================================================================== escriptize(Config, AppFile) -> %% Extract the application name from the archive -- this is the default %% name of the generated script AppName = rebar_app_utils:app_name(AppFile), %% Get the output filename for the escript -- this may include dirs Filename = rebar_config:get_local(Config, escript_name, AppName), ok = filelib:ensure_dir(Filename), %% Look for a list of other applications (dependencies) to include %% in the output file. We then use the .app files for each of these %% to pull in all the .beam files. InclBeams = get_app_beams( rebar_config:get_local(Config, escript_incl_apps, []), []), %% Construct the archive of everything in ebin/ dir -- put it on the %% top-level of the zip file so that code loading works properly. Files = load_files("*", "ebin") ++ InclBeams, case zip:create("mem", Files, [memory]) of {ok, {"mem", ZipBin}} -> %% Archive was successfully created. Prefix that binary with our %% header and write to our escript file Shebang = rebar_config:get(Config, escript_shebang, "#!/usr/bin/env escript\n"), Comment = rebar_config:get(Config, escript_comment, "%%\n"), EmuArgs = rebar_config:get(Config, escript_emu_args, "%%!\n"), Script = iolist_to_binary([Shebang, Comment, EmuArgs, ZipBin]), case file:write_file(Filename, Script) of ok -> ok; {error, WriteError} -> ?ERROR("Failed to write ~p script: ~p\n", [AppName, WriteError]), ?FAIL end; {error, ZipError} -> ?ERROR("Failed to construct ~p escript: ~p\n", [AppName, ZipError]), ?FAIL 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#00100), ok. clean(Config, AppFile) -> %% Extract the application name from the archive -- this is the default %% name of the generated script AppName = rebar_app_utils:app_name(AppFile), %% Get the output filename for the escript -- this may include dirs Filename = rebar_config:get_local(Config, escript_name, AppName), rebar_file_utils:delete_each([Filename]). %% =================================================================== %% Internal functions %% =================================================================== get_app_beams([], Acc) -> Acc; get_app_beams([App | Rest], Acc) -> case code:lib_dir(App, ebin) of {error, bad_name} -> ?ABORT("Failed to get ebin/ directory for " "~p escript_incl_apps.", [App]); Path -> Acc2 = [{filename:join([App, ebin, F]), file_contents(filename:join(Path, F))} || F <- filelib:wildcard("*", Path)], get_app_beams(Rest, Acc2 ++ Acc) end. load_files(Wildcard, Dir) -> [read_file(Filename, Dir) || Filename <- filelib:wildcard(Wildcard, Dir)]. read_file(Filename, Dir) -> {Filename, file_contents(filename:join(Dir, Filename))}. file_contents(Filename) -> {ok, Bin} = file:read_file(Filename), Bin. rebar-2.0.0/src/rebar_eunit.erl000066400000000000000000000574771176314626300164330ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% @author Dave Smith %% @doc rebar_eunit supports the following commands: %%
    %%
  • eunit - runs eunit tests
  • %%
  • clean - remove .eunit directory
  • %%
  • reset_after_eunit::boolean() - default = true. %% If true, try to "reset" VM state to approximate state prior to %% running the EUnit tests: %%
      %%
    • Stop net_kernel if it was started
    • %%
    • Stop OTP applications not running before EUnit tests were run
    • %%
    • Kill processes not running before EUnit tests were run
    • %%
    • Reset OTP application environment variables
    • %%
    %%
  • %%
%% The following Global options are supported: %%
    %%
  • verbose=1 - show extra output from the eunit test
  • %%
  • suite="foo"" - runs test/foo_tests.erl
  • %%
%% Additionally, for projects that have separate folders for the core %% implementation, and for the unit tests, then the following %% rebar.config option can be provided: %% {eunit_compile_opts, [{src_dirs, ["dir"]}]}.. %% @copyright 2009, 2010 Dave Smith %% ------------------------------------------------------------------- -module(rebar_eunit). -export([eunit/2, clean/2]). -include("rebar.hrl"). -define(EUNIT_DIR, ".eunit"). %% =================================================================== %% Public API %% =================================================================== eunit(Config, _AppFile) -> %% Make sure ?EUNIT_DIR/ and ebin/ directory exists (tack on dummy module) ok = filelib:ensure_dir(eunit_dir() ++ "/foo"), ok = filelib:ensure_dir(ebin_dir() ++ "/foo"), %% Setup code path prior to compilation so that parse_transforms %% and the like work properly. Also, be sure to add ebin_dir() %% to the END of the code path so that we don't have to jump %% through hoops to access the .app file CodePath = code:get_path(), true = code:add_patha(eunit_dir()), true = code:add_pathz(ebin_dir()), %% Obtain all the test modules for inclusion in the compile stage. %% Notice: this could also be achieved with the following %% rebar.config option: {eunit_compile_opts, [{src_dirs, ["test"]}]} TestErls = rebar_utils:find_files("test", ".*\\.erl\$"), %% Copy source files to eunit dir for cover in case they are not directly %% in src but in a subdirectory of src. Cover only looks in cwd and ../src %% for source files. SrcErls = rebar_utils:find_files("src", ".*\\.erl\$"), %% If it is not the first time rebar eunit is executed, there will be source %% files already present in ?EUNIT_DIR. Since some SCMs (like Perforce) set %% the source files as being read only (unless they are checked out), we %% need to be sure that the files already present in ?EUNIT_DIR are writable %% before doing the copy. This is done here by removing any file that was %% already present before calling rebar_file_utils:cp_r. %% Get the full path to a file that was previously copied in ?EUNIT_DIR ToCleanUp = fun(F, Acc) -> F2 = filename:basename(F), F3 = filename:join([?EUNIT_DIR, F2]), case filelib:is_regular(F3) of true -> [F3|Acc]; false -> Acc end end, ok = rebar_file_utils:delete_each(lists:foldl(ToCleanUp, [], TestErls)), ok = rebar_file_utils:delete_each(lists:foldl(ToCleanUp, [], SrcErls)), ok = rebar_file_utils:cp_r(SrcErls ++ TestErls, ?EUNIT_DIR), %% Compile erlang code to ?EUNIT_DIR, using a tweaked config %% with appropriate defines for eunit, and include all the test modules %% as well. rebar_erlc_compiler:doterl_compile(eunit_config(Config), ?EUNIT_DIR, TestErls), %% Build a list of all the .beams in ?EUNIT_DIR -- use this for %% cover and eunit testing. Normally you can just tell cover %% and/or eunit to scan the directory for you, but eunit does a %% code:purge in conjunction with that scan and causes any cover %% compilation info to be lost. Filter out "*_tests" modules so %% eunit won't doubly run them and so cover only calculates %% coverage on production code. However, keep "*_tests" modules %% that are not automatically included by eunit. AllBeamFiles = rebar_utils:beams(?EUNIT_DIR), {BeamFiles, TestBeamFiles} = lists:partition(fun(N) -> string:str(N, "_tests.beam") =:= 0 end, AllBeamFiles), OtherBeamFiles = TestBeamFiles -- [filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles], ModuleBeamFiles = BeamFiles ++ OtherBeamFiles, Modules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- ModuleBeamFiles], SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls], {ok, CoverLog} = cover_init(Config, ModuleBeamFiles), StatusBefore = status_before_eunit(), EunitResult = perform_eunit(Config, Modules), perform_cover(Config, Modules, SrcModules), cover_close(CoverLog), case proplists:get_value(reset_after_eunit, get_eunit_opts(Config), true) of true -> reset_after_eunit(StatusBefore); false -> ok end, case EunitResult of ok -> ok; _ -> ?ABORT("One or more eunit tests failed.~n", []) end, %% Restore code path true = code:set_path(CodePath), ok. clean(_Config, _File) -> rebar_file_utils:rm_rf(?EUNIT_DIR). %% =================================================================== %% Internal functions %% =================================================================== eunit_dir() -> filename:join(rebar_utils:get_cwd(), ?EUNIT_DIR). ebin_dir() -> filename:join(rebar_utils:get_cwd(), "ebin"). perform_eunit(Config, Modules) -> %% suite defined, so only specify the module that relates to the %% suite (if any). Suite can be a comma seperated list of modules to run. Suite = rebar_config:get_global(suite, undefined), EunitOpts = get_eunit_opts(Config), %% Move down into ?EUNIT_DIR while we run tests so any generated files %% are created there (versus in the source dir) Cwd = rebar_utils:get_cwd(), ok = file:set_cwd(?EUNIT_DIR), EunitResult = perform_eunit(EunitOpts, Modules, Suite), %% Return to original working dir ok = file:set_cwd(Cwd), EunitResult. perform_eunit(EunitOpts, Modules, undefined) -> (catch eunit:test(Modules, EunitOpts)); perform_eunit(EunitOpts, _Modules, Suites) -> (catch eunit:test([list_to_atom(Suite) || Suite <- string:tokens(Suites, ",")], EunitOpts)). get_eunit_opts(Config) -> %% Enable verbose in eunit if so requested.. BaseOpts = case rebar_config:is_verbose() of true -> [verbose]; false -> [] end, BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []). eunit_config(Config) -> EqcOpts = eqc_opts(), PropErOpts = proper_opts(), ErlOpts = rebar_config:get_list(Config, erl_opts, []), EunitOpts = rebar_config:get_list(Config, eunit_compile_opts, []), Opts0 = [{d, 'TEST'}] ++ ErlOpts ++ EunitOpts ++ EqcOpts ++ PropErOpts, Opts = [O || O <- Opts0, O =/= no_debug_info], Config1 = rebar_config:set(Config, erl_opts, Opts), FirstErls = rebar_config:get_list(Config1, eunit_first_files, []), rebar_config:set(Config1, erl_first_files, FirstErls). eqc_opts() -> define_if('EQC', is_lib_avail(is_eqc_avail, eqc, "eqc.hrl", "QuickCheck")). proper_opts() -> define_if('PROPER', is_lib_avail(is_proper_avail, proper, "proper.hrl", "PropEr")). define_if(Def, true) -> [{d, Def}]; define_if(_Def, false) -> []. is_lib_avail(DictKey, Mod, Hrl, Name) -> case erlang:get(DictKey) of undefined -> IsAvail = case code:lib_dir(Mod, include) of {error, bad_name} -> false; Dir -> filelib:is_regular(filename:join(Dir, Hrl)) end, erlang:put(DictKey, IsAvail), ?DEBUG("~s availability: ~p\n", [Name, IsAvail]), IsAvail; IsAvail -> IsAvail end. perform_cover(Config, BeamFiles, SrcModules) -> perform_cover(rebar_config:get(Config, cover_enabled, false), Config, BeamFiles, SrcModules). perform_cover(false, _Config, _BeamFiles, _SrcModules) -> ok; perform_cover(true, Config, BeamFiles, SrcModules) -> cover_analyze(Config, BeamFiles, SrcModules). cover_analyze(_Config, [], _SrcModules) -> ok; cover_analyze(Config, Modules, SrcModules) -> %% suite can be a comma seperated list of modules to test Suite = [list_to_atom(S) || S <- string:tokens(rebar_config:get_global(suite, ""), ",")], FilteredModules = case Suite of [] -> Modules; _ -> [M || M <- Modules, lists:member(M, Suite)] end, %% Generate coverage info for all the cover-compiled modules Coverage = lists:flatten([cover_analyze_mod(M) || M <- FilteredModules]), %% Write index of coverage info cover_write_index(lists:sort(Coverage), SrcModules), %% Write coverage details for each file lists:foreach(fun({M, _, _}) -> {ok, _} = cover:analyze_to_file(M, cover_file(M), [html]) end, Coverage), Index = filename:join([rebar_utils:get_cwd(), ?EUNIT_DIR, "index.html"]), ?CONSOLE("Cover analysis: ~s\n", [Index]), %% Print coverage report, if configured case rebar_config:get(Config, cover_print_enabled, false) of true -> cover_print_coverage(lists:sort(Coverage)); false -> ok end. cover_close(not_enabled) -> ok; cover_close(F) -> ok = file:close(F). cover_init(false, _BeamFiles) -> {ok, not_enabled}; cover_init(true, BeamFiles) -> %% Attempt to start the cover server, then set it's group leader to %% .eunit/cover.log, so all cover log messages will go there instead of %% to stdout. If the cover server is already started we'll reuse that %% pid. {ok, CoverPid} = case cover:start() of {ok, _P} = OkStart -> OkStart; {error,{already_started, P}} -> {ok, P}; {error, _Reason} = ErrorStart -> ErrorStart end, {ok, F} = OkOpen = file:open( filename:join([?EUNIT_DIR, "cover.log"]), [write]), group_leader(F, CoverPid), %% Make sure any previous runs of cover don't unduly influence cover:reset(), ?INFO("Cover compiling ~s\n", [rebar_utils:get_cwd()]), Compiled = [{Beam, cover:compile_beam(Beam)} || Beam <- BeamFiles], case [Module || {_, {ok, Module}} <- Compiled] of [] -> %% No modules compiled successfully...fail ?ERROR("Cover failed to compile any modules; aborting.~n", []), ?FAIL; _ -> %% At least one module compiled successfully %% It's not an error for cover compilation to fail partially, %% but we do want to warn about them PrintWarning = fun(Beam, Desc) -> ?CONSOLE("Cover compilation warning for ~p: ~p", [Beam, Desc]) end, _ = [PrintWarning(Beam, Desc) || {Beam, {error, Desc}} <- Compiled], OkOpen end; cover_init(Config, BeamFiles) -> cover_init(rebar_config:get(Config, cover_enabled, false), BeamFiles). cover_analyze_mod(Module) -> case cover:analyze(Module, coverage, module) of {ok, {Module, {Covered, NotCovered}}} -> %% Modules that include the eunit header get an implicit %% test/0 fun, which cover considers a runnable line, but %% eunit:test(TestRepresentation) never calls. Decrement %% NotCovered in this case. [align_notcovered_count(Module, Covered, NotCovered, is_eunitized(Module))]; {error, Reason} -> ?ERROR("Cover analyze failed for ~p: ~p ~p\n", [Module, Reason, code:which(Module)]), [] end. is_eunitized(Mod) -> has_eunit_test_fun(Mod) andalso has_header(Mod, "include/eunit.hrl"). has_eunit_test_fun(Mod) -> [F || {exports, Funs} <- Mod:module_info(), {F, 0} <- Funs, F =:= test] =/= []. has_header(Mod, Header) -> Mod1 = case code:which(Mod) of cover_compiled -> {file, File} = cover:is_compiled(Mod), File; non_existing -> Mod; preloaded -> Mod; L -> L end, {ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Mod1, [abstract_code]), [F || {attribute, 1, file, {F, 1}} <- AC, string:str(F, Header) =/= 0] =/= []. align_notcovered_count(Module, Covered, NotCovered, false) -> {Module, Covered, NotCovered}; align_notcovered_count(Module, Covered, NotCovered, true) -> {Module, Covered, NotCovered - 1}. cover_write_index(Coverage, SrcModules) -> {ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]), ok = file:write(F, "Coverage Summary\n"), IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end, {SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage), cover_write_index_section(F, "Source", SrcCoverage), cover_write_index_section(F, "Test", TestCoverage), ok = file:write(F, ""), ok = file:close(F). cover_write_index_section(_F, _SectionName, []) -> ok; cover_write_index_section(F, SectionName, Coverage) -> %% Calculate total coverage % {Covered, NotCovered} = lists:foldl(fun({_Mod, C, N}, {CAcc, NAcc}) -> {CAcc + C, NAcc + N} end, {0, 0}, Coverage), TotalCoverage = percentage(Covered, NotCovered), %% Write the report ok = file:write(F, ?FMT("

~s Summary

\n", [SectionName])), ok = file:write(F, ?FMT("

Total: ~s

\n", [TotalCoverage])), ok = file:write(F, "\n"), FmtLink = fun(Module, Cov, NotCov) -> ?FMT("\n", [Module, Module, percentage(Cov, NotCov)]) end, lists:foreach(fun({Module, Cov, NotCov}) -> ok = file:write(F, FmtLink(Module, Cov, NotCov)) end, Coverage), ok = file:write(F, "
ModuleCoverage %
~s~s
\n"). cover_print_coverage(Coverage) -> {Covered, NotCovered} = lists:foldl(fun({_Mod, C, N}, {CAcc, NAcc}) -> {CAcc + C, NAcc + N} end, {0, 0}, Coverage), TotalCoverage = percentage(Covered, NotCovered), %% Determine the longest module name for right-padding Width = lists:foldl(fun({Mod, _, _}, Acc) -> case length(atom_to_list(Mod)) of N when N > Acc -> N; _ -> Acc end end, 0, Coverage) * -1, %% Print the output the console ?CONSOLE("~nCode Coverage:~n", []), lists:foreach(fun({Mod, C, N}) -> ?CONSOLE("~*s : ~3s~n", [Width, Mod, percentage(C, N)]) end, Coverage), ?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]). cover_file(Module) -> filename:join([?EUNIT_DIR, atom_to_list(Module) ++ ".COVER.html"]). percentage(0, 0) -> "not executed"; percentage(Cov, NotCov) -> integer_to_list(trunc((Cov / (Cov + NotCov)) * 100)) ++ "%". get_app_names() -> [AppName || {AppName, _, _} <- application:loaded_applications()]. status_before_eunit() -> Apps = get_app_names(), AppEnvs = [{App, application:get_all_env(App)} || App <- Apps], {erlang:processes(), erlang:is_alive(), AppEnvs, ets:tab2list(ac_tab)}. reset_after_eunit({OldProcesses, WasAlive, OldAppEnvs, _OldACs}) -> IsAlive = erlang:is_alive(), if not WasAlive andalso IsAlive -> ?DEBUG("Stopping net kernel....\n", []), erl_epmd:stop(), _ = net_kernel:stop(), pause_until_net_kernel_stopped(); true -> ok end, OldApps = [App || {App, _} <- OldAppEnvs], Apps = get_app_names(), _ = [begin _ = case lists:member(App, OldApps) of true -> ok; false -> application:stop(App) end, ok = application:unset_env(App, K) end || App <- Apps, App /= rebar, {K, _V} <- application:get_all_env(App)], reconstruct_app_env_vars(Apps), Processes = erlang:processes(), _ = kill_extras(Processes -- OldProcesses), ok. kill_extras(Pids) -> %% Killing any of the procs below will either: %% 1. Interfere with stuff that we don't want interfered with, or %% 2. May/will force the 'kernel' app to shutdown, which *will* %% interfere with rebar's ability To Do Useful Stuff(tm). %% This list may require changes as OTP versions and/or %% rebar use cases change. KeepProcs = [cover_server, eunit_server, eqc, eqc_license, eqc_locked, %% inet_gethost_native is started on demand, when %% doing name lookups. It is under kernel_sup, under %% a supervisor_bridge. inet_gethost_native], Killed = [begin Info = case erlang:process_info(Pid) of undefined -> []; Else -> Else end, Keep1 = case proplists:get_value(registered_name, Info) of undefined -> false; Name -> lists:member(Name, KeepProcs) end, Keep2 = case proplists:get_value(dictionary, Info) of undefined -> false; Ds -> case proplists:get_value('$ancestors', Ds) of undefined -> false; As -> lists:member(kernel_sup, As) end end, if Keep1 orelse Keep2 -> ok; true -> ?DEBUG("Kill ~p ~p\n", [Pid, Info]), exit(Pid, kill), Pid end end || Pid <- Pids], case lists:usort(Killed) -- [ok] of [] -> ?DEBUG("No processes to kill\n", []), []; Else -> lists:foreach(fun(Pid) -> wait_until_dead(Pid) end, Else), Else end. reconstruct_app_env_vars([App|Apps]) -> CmdLine0 = proplists:get_value(App, init:get_arguments(), []), CmdVars = [{list_to_atom(K), list_to_atom(V)} || {K, V} <- CmdLine0], AppFile = (catch filename:join([code:lib_dir(App), "ebin", atom_to_list(App) ++ ".app"])), AppVars = case file:consult(AppFile) of {ok, [{application, App, Ps}]} -> proplists:get_value(env, Ps, []); _ -> [] end, %% App vars specified in config files override those in the .app file. %% Config files later in the args list override earlier ones. AppVars1 = case init:get_argument(config) of {ok, ConfigFiles} -> {App, MergedAppVars} = lists:foldl(fun merge_app_vars/2, {App, AppVars}, ConfigFiles), MergedAppVars; error -> AppVars end, AllVars = CmdVars ++ AppVars1, ?DEBUG("Reconstruct ~p ~p\n", [App, AllVars]), lists:foreach(fun({K, V}) -> application:set_env(App, K, V) end, AllVars), reconstruct_app_env_vars(Apps); reconstruct_app_env_vars([]) -> ok. merge_app_vars(ConfigFile, {App, AppVars}) -> File = ensure_config_extension(ConfigFile), FileAppVars = app_vars_from_config_file(File, App), Dict1 = dict:from_list(AppVars), Dict2 = dict:from_list(FileAppVars), Dict3 = dict:merge(fun(_Key, _Value1, Value2) -> Value2 end, Dict1, Dict2), {App, dict:to_list(Dict3)}. ensure_config_extension(File) -> %% config files must end with .config on disk but when specifying them %% via the -config option the extension is optional BaseFileName = filename:basename(File, ".config"), DirName = filename:dirname(File), filename:join(DirName, BaseFileName ++ ".config"). app_vars_from_config_file(File, App) -> case file:consult(File) of {ok, [Env]} -> proplists:get_value(App, Env, []); _ -> [] end. wait_until_dead(Pid) when is_pid(Pid) -> Ref = erlang:monitor(process, Pid), receive {'DOWN', Ref, process, _Obj, Info} -> Info after 10*1000 -> exit({timeout_waiting_for, Pid}) end; wait_until_dead(_) -> ok. pause_until_net_kernel_stopped() -> pause_until_net_kernel_stopped(10). pause_until_net_kernel_stopped(0) -> exit(net_kernel_stop_failed); pause_until_net_kernel_stopped(N) -> try _ = net_kernel:i(), timer:sleep(100), pause_until_net_kernel_stopped(N - 1) catch error:badarg -> ?DEBUG("Stopped net kernel.\n", []), ok end. rebar-2.0.0/src/rebar_file_utils.erl000066400000000000000000000157551176314626300174370ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_file_utils). -export([rm_rf/1, cp_r/2, mv/2, delete_each/1]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== %% @doc Remove files and directories. %% Target is a single filename, directoryname or wildcard expression. -spec rm_rf(Target::string()) -> ok. rm_rf(Target) -> case os:type() of {unix, _} -> EscTarget = escape_spaces(Target), {ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]), [{use_stdout, false}, return_on_error]), ok; {win32, _} -> Filelist = filelib:wildcard(Target), Dirs = [F || F <- Filelist, filelib:is_dir(F)], Files = Filelist -- Dirs, ok = delete_each(Files), ok = delete_each_dir_win32(Dirs), ok end. -spec cp_r(Sources::list(string()), Dest::file:filename()) -> ok. cp_r(Sources, Dest) -> case os:type() of {unix, _} -> EscSources = [escape_spaces(Src) || Src <- Sources], SourceStr = string:join(EscSources, " "), {ok, []} = rebar_utils:sh(?FMT("cp -R ~s \"~s\"", [SourceStr, Dest]), [{use_stdout, false}, return_on_error]), ok; {win32, _} -> lists:foreach(fun(Src) -> ok = cp_r_win32(Src,Dest) end, Sources), ok end. -spec mv(Source::string(), Dest::file:filename()) -> ok. mv(Source, Dest) -> case os:type() of {unix, _} -> EscSource = escape_spaces(Source), EscDest = escape_spaces(Dest), {ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), [{use_stdout, false}, return_on_error]), ok; {win32, _} -> {ok, R} = rebar_utils:sh( ?FMT("move /y \"~s\" \"~s\" 1> nul", [filename:nativename(Source), filename:nativename(Dest)]), [{use_stdout, false}, return_on_error]), case R of [] -> ok; _ -> {error, lists:flatten( io_lib:format("Failed to move ~s to ~s~n", [Source, Dest]))} end end. delete_each([]) -> ok; delete_each([File | Rest]) -> case file:delete(File) of ok -> delete_each(Rest); {error, enoent} -> delete_each(Rest); {error, Reason} -> ?ERROR("Failed to delete file ~s: ~p\n", [File, Reason]), ?FAIL end. %% =================================================================== %% Internal functions %% =================================================================== delete_each_dir_win32([]) -> ok; delete_each_dir_win32([Dir | Rest]) -> {ok, []} = rebar_utils:sh(?FMT("rd /q /s \"~s\"", [filename:nativename(Dir)]), [{use_stdout, false}, return_on_error]), delete_each_dir_win32(Rest). xcopy_win32(Source,Dest)-> {ok, R} = rebar_utils:sh( ?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul", [filename:nativename(Source), filename:nativename(Dest)]), [{use_stdout, false}, return_on_error]), case length(R) > 0 of %% when xcopy fails, stdout is empty and and error message is printed %% to stderr (which is redirected to nul) true -> ok; false -> {error, lists:flatten( io_lib:format("Failed to xcopy from ~s to ~s~n", [Source, Dest]))} end. cp_r_win32({true, SourceDir}, {true, DestDir}) -> %% from directory to directory SourceBase = filename:basename(SourceDir), ok = case file:make_dir(filename:join(DestDir, SourceBase)) of {error, eexist} -> ok; Other -> Other end, ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase)); cp_r_win32({false, Source} = S,{true, DestDir}) -> %% from file to directory cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))}); cp_r_win32({false, Source},{false, Dest}) -> %% from file to file {ok,_} = file:copy(Source, Dest), ok; cp_r_win32({true, SourceDir}, {false, DestDir}) -> case filelib:is_regular(DestDir) of true -> %% From directory to file? This shouldn't happen {error, lists:flatten( io_lib:format("Cannot copy dir (~p) to file (~p)\n", [SourceDir, DestDir]))}; false -> %% Specifying a target directory that doesn't currently exist. %% So let's attempt to create this directory case filelib:ensure_dir(filename:join(DestDir, "dummy")) of ok -> ok = xcopy_win32(SourceDir, DestDir); {error, Reason} -> {error, lists:flatten( io_lib:format("Unable to create dir ~p: ~p\n", [DestDir, Reason]))} end end; cp_r_win32(Source,Dest) -> Dst = {filelib:is_dir(Dest), Dest}, lists:foreach(fun(Src) -> ok = cp_r_win32({filelib:is_dir(Src), Src}, Dst) end, filelib:wildcard(Source)), ok. escape_spaces(Str) -> re:replace(Str, " ", "\\\\ ", [global, {return, list}]). rebar-2.0.0/src/rebar_lfe_compiler.erl000066400000000000000000000056221176314626300177300ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com), %% Tim Dysinger (tim@dysinger.net) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_lfe_compiler). -export([compile/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== compile(Config, _AppFile) -> FirstFiles = rebar_config:get_list(Config, lfe_first_files, []), rebar_base_compiler:run(Config, FirstFiles, "src", ".lfe", "ebin", ".beam", fun compile_lfe/3). %% =================================================================== %% Internal functions %% =================================================================== compile_lfe(Source, _Target, Config) -> case code:which(lfe_comp) of non_existing -> ?ERROR("~n" "*** MISSING LFE COMPILER ***~n" " You must do one of the following:~n" " a) Install LFE globally in your erl libs~n" " b) Add LFE as a dep for your project, eg:~n" " {lfe, \"0.6.1\",~n" " {git, \"git://github.com/rvirding/lfe\",~n" " {tag, \"v0.6.1\"}}}~n" "~n", []), ?FAIL; _ -> Opts = [{i, "include"}, {outdir, "ebin"}, report] ++ rebar_config:get_list(Config, erl_opts, []), case lfe_comp:file(Source, Opts) of {ok, _} -> ok; _ -> ?FAIL end end. rebar-2.0.0/src/rebar_log.erl000066400000000000000000000060341176314626300160470ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_log). -export([init/0, set_level/1, get_level/0, default_level/0, log/3]). %% =================================================================== %% Public API %% =================================================================== init() -> case valid_level(rebar_config:get_global(verbose, error_level())) of 0 -> set_level(error); 1 -> set_level(warn); 2 -> set_level(info); 3 -> set_level(debug) end. set_level(Level) -> ok = application:set_env(rebar, log_level, Level). get_level() -> case application:get_env(rebar, log_level) of undefined -> error; {ok, Value} -> Value end. log(Level, Str, Args) -> {ok, LogLevel} = application:get_env(rebar, log_level), case should_log(LogLevel, Level) of true -> io:format(log_prefix(Level) ++ Str, Args); false -> ok end. default_level() -> error_level(). %% =================================================================== %% Internal functions %% =================================================================== valid_level(Level) -> erlang:max(error_level(), erlang:min(Level, debug_level())). error_level() -> 0. debug_level() -> 3. should_log(debug, _) -> true; should_log(info, debug) -> false; should_log(info, _) -> true; should_log(warn, debug) -> false; should_log(warn, info) -> false; should_log(warn, _) -> true; should_log(error, error) -> true; should_log(error, _) -> false; should_log(_, _) -> false. log_prefix(debug) -> "DEBUG: "; log_prefix(info) -> "INFO: "; log_prefix(warn) -> "WARN: "; log_prefix(error) -> "ERROR: ". rebar-2.0.0/src/rebar_neotoma_compiler.erl000066400000000000000000000133771176314626300206320ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2010 Cliff Moon (cliff@moonpolysoft.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% The rebar_neotoma module is a plugin for rebar that compiles %% neotoma peg files. By default, it compiles all src/*.peg to src/*.erl %% %% Configuration options should be placed in rebar.config under %% neotoma_opts. Available options include: %% %% doc_root: where to find the peg files to compile. %% "src" by default %% out_dir: where to put the generated erl files. %% "src" by defualt %% module_ext: characters to append to the module's name. %% "" by default %% source_ext: extension of peg source files -module(rebar_neotoma_compiler). -export([compile/2]). -include("rebar.hrl"). %% ============================================================================ %% Public API %% ============================================================================ compile(Config, _AppFile) -> NeoOpts = neotoma_opts(Config), rebar_base_compiler:run(Config, [], option(doc_root, NeoOpts), ".peg", option(out_dir, NeoOpts), option(module_ext, NeoOpts) ++ ".beam", fun compile_neo/3, [{check_last_mod,false}]). %% ============================================================================ %% Public API %% ============================================================================ neotoma_opts(Config) -> rebar_config:get(Config, neotoma_opts, []). option(Opt, Options) -> proplists:get_value(Opt, Options, default(Opt)). default(doc_root) -> "src"; default(out_dir) -> "src"; default(module_ext) -> ""; default(source_ext) -> ".peg". compile_neo(Source, Target, Config) -> case code:which(neotoma) of non_existing -> ?ERROR("~n===============================================~n" " You need to install neotoma to compile PEG grammars~n" " Download the latest tarball release from github~n" " https://github.com/seancribbs/neotoma~n" " and install it into your erlang library dir~n" "===============================================~n~n", []), ?FAIL; _ -> case needs_compile(Source, Target, Config) of true -> do_compile(Source, Target, Config); false -> skipped end end. do_compile(Source, _Target, Config) -> %% TODO: Check last mod on target and referenced DTLs here.. NeoOpts = neotoma_opts(Config), %% ensure that doc_root and out_dir are defined, %% using defaults if necessary Opts = [{output, option(out_dir, NeoOpts)}, {module, list_to_atom(filename:basename(Source, ".peg") ++ option(module_ext, NeoOpts))}], case neotoma:file(Source, Opts ++ NeoOpts) of ok -> ok; Reason -> ?ERROR("Compiling peg ~s failed:~n ~p~n", [Source, Reason]), ?FAIL end. needs_compile(Source, Target, Config) -> LM = filelib:last_modified(Target), LM < filelib:last_modified(Source) orelse lists:any(fun(D) -> LM < filelib:last_modified(D) end, referenced_pegs(Source, Config)). referenced_pegs(Source, Config) -> Set = referenced_pegs1([Source], Config, sets:add_element(Source, sets:new())), sets:to_list(sets:del_element(Source, Set)). referenced_pegs1(Step, Config, Seen) -> NeoOpts = neotoma_opts(Config), ExtMatch = re:replace(option(source_ext, NeoOpts), "\.", "\\\\\\\\.", [{return, list}]), ShOpts = [{use_stdout, false}, return_on_error], AllRefs = lists:append( [begin Cmd = lists:flatten(["grep -o [^\\\"]*", ExtMatch, " ", F]), case rebar_utils:sh(Cmd, ShOpts) of {ok, Res} -> string:tokens(Res, "\n"); {error, _} -> "" end end || F <- Step]), DocRoot = option(doc_root, NeoOpts), WithPaths = [ filename:join([DocRoot, F]) || F <- AllRefs ], Existing = [F || F <- WithPaths, filelib:is_regular(F)], New = sets:subtract(sets:from_list(Existing), Seen), case sets:size(New) of 0 -> Seen; _ -> referenced_pegs1(sets:to_list(New), Config, sets:union(New, Seen)) end. rebar-2.0.0/src/rebar_otp_app.erl000066400000000000000000000157761176314626300167450ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_otp_app). -export([compile/2, clean/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== compile(Config, File) -> %% If we get an .app.src file, it needs to be pre-processed and %% written out as a ebin/*.app file. That resulting file will then %% be validated as usual. AppFile = case rebar_app_utils:is_app_src(File) of true -> preprocess(Config, File); false -> File end, %% Load the app file and validate it. case rebar_app_utils:load_app_file(AppFile) of {ok, AppName, AppData} -> validate_name(AppName, AppFile), %% In general, the list of modules is an important thing to validate %% for compliance with OTP guidelines and upgrade procedures. %% However, some people prefer not to validate this list. case rebar_config:get_local(Config, validate_app_modules, true) of true -> validate_modules(AppName, proplists:get_value(modules, AppData)); false -> ok end; {error, Reason} -> ?ABORT("Failed to load app file ~s: ~p\n", [AppFile, Reason]) end. clean(_Config, File) -> %% If the app file is a .app.src, delete the generated .app file case rebar_app_utils:is_app_src(File) of true -> case file:delete(rebar_app_utils:app_src_to_app(File)) of ok -> ok; {error, enoent} -> %% The file not existing is OK, we can ignore the error. ok; Other -> Other end; false -> ok end. %% =================================================================== %% Internal functions %% =================================================================== preprocess(Config, AppSrcFile) -> case rebar_app_utils:load_app_file(AppSrcFile) of {ok, AppName, AppData} -> %% Look for a configuration file with vars we want to %% substitute. Note that we include the list of modules available in %% ebin/ and update the app data accordingly. AppVars = load_app_vars(Config) ++ [{modules, ebin_modules()}], A1 = apply_app_vars(AppVars, AppData), %% AppSrcFile may contain instructions for generating a vsn number Vsn = rebar_app_utils:app_vsn(AppSrcFile), A2 = lists:keystore(vsn, 1, A1, {vsn, Vsn}), %% Build the final spec as a string Spec = io_lib:format("~p.\n", [{application, AppName, A2}]), %% Setup file .app filename and write new contents AppFile = rebar_app_utils:app_src_to_app(AppSrcFile), ok = file:write_file(AppFile, Spec), %% Make certain that the ebin/ directory is available %% on the code path true = code:add_path(filename:absname(filename:dirname(AppFile))), AppFile; {error, Reason} -> ?ABORT("Failed to read ~s for preprocessing: ~p\n", [AppSrcFile, Reason]) end. load_app_vars(Config) -> case rebar_config:get_local(Config, app_vars_file, undefined) of undefined -> ?INFO("No app_vars_file defined.\n", []), []; Filename -> ?INFO("Loading app vars from ~p\n", [Filename]), {ok, Vars} = file:consult(Filename), Vars end. apply_app_vars([], AppData) -> AppData; apply_app_vars([{Key, Value} | Rest], AppData) -> AppData2 = lists:keystore(Key, 1, AppData, {Key, Value}), apply_app_vars(Rest, AppData2). validate_name(AppName, File) -> %% Convert the .app file name to an atom -- check it against the %% identifier within the file ExpApp = list_to_atom(filename:basename(File, ".app")), case ExpApp == AppName of true -> ok; false -> ?ERROR("Invalid ~s: name of application (~p) " "must match filename.\n", [File, AppName]), ?FAIL end. validate_modules(AppName, undefined) -> ?ERROR("Missing modules declaration in ~p.app~n", [AppName]), ?FAIL; validate_modules(AppName, Mods) -> %% Construct two sets -- one for the actual .beam files in ebin/ %% and one for the modules %% listed in the .app file EbinSet = ordsets:from_list(ebin_modules()), ModSet = ordsets:from_list(Mods), %% Identify .beam files listed in the .app, but not present in ebin/ case ordsets:subtract(ModSet, EbinSet) of [] -> ok; MissingBeams -> Msg1 = lists:flatten([io_lib:format("\t* ~p\n", [M]) || M <- MissingBeams]), ?ERROR("One or more modules listed in ~p.app are not " "present in ebin/*.beam:\n~s", [AppName, Msg1]), ?FAIL end, %% Identify .beam files NOT list in the .app, but present in ebin/ case ordsets:subtract(EbinSet, ModSet) of [] -> ok; MissingMods -> Msg2 = lists:flatten([io_lib:format("\t* ~p\n", [M]) || M <- MissingMods]), ?ERROR("One or more .beam files exist that are not " "listed in ~p.app:\n~s", [AppName, Msg2]), ?FAIL end. ebin_modules() -> lists:sort([rebar_utils:beam_to_mod("ebin", N) || N <- rebar_utils:beams("ebin")]). rebar-2.0.0/src/rebar_port_compiler.erl000066400000000000000000000603421176314626300201460ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_port_compiler). -export([compile/2, clean/2, setup_env/1]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== %% Supported configuration variables: %% %% * port_specs - Erlang list of tuples of the forms %% {ArchRegex, TargetFile, Sources, Options} %% {ArchRegex, TargetFile, Sources} %% {TargetFile, Sources} %% %% * port_env - Erlang list of key/value pairs which will control %% the environment when running the compiler and linker. %% %% By default, the following variables are defined: %% CC - C compiler %% CXX - C++ compiler %% CFLAGS - C compiler %% CXXFLAGS - C++ compiler %% LDFLAGS - Link flags %% ERL_CFLAGS - default -I paths for erts and ei %% ERL_LDFLAGS - default -L and -lerl_interface -lei %% DRV_CFLAGS - flags that will be used for compiling %% DRV_LDFLAGS - flags that will be used for linking %% EXE_CFLAGS - flags that will be used for compiling %% EXE_LDFLAGS - flags that will be used for linking %% ERL_EI_LIBDIR - ei library directory %% DRV_CXX_TEMPLATE - C++ command template %% DRV_CC_TEMPLATE - C command template %% DRV_LINK_TEMPLATE - Linker command template %% EXE_CXX_TEMPLATE - C++ command template %% EXE_CC_TEMPLATE - C command template %% EXE_LINK_TEMPLATE - Linker command template %% PORT_IN_FILES - contains a space separated list of input %% file(s), (used in command template) %% PORT_OUT_FILE - contains the output filename (used in %% command template) %% %% Note that if you wish to extend (vs. replace) these variables, %% you MUST include a shell-style reference in your definition. %% e.g. to extend CFLAGS, do something like: %% %% {port_env, [{"CFLAGS", "$CFLAGS -MyOtherOptions"}]} %% %% It is also possible to specify platform specific options %% by specifying a triplet where the first string is a regex %% that is checked against Erlang's system architecture string. %% e.g. to specify a CFLAG that only applies to x86_64 on linux %% do: %% %% {port_env, [{"x86_64.*-linux", "CFLAGS", %% "$CFLAGS -X86Options"}]} %% %% TODO: reconsider keeping both sources and objects once %% support for deprecated options has been remove. %% remove [] as valid value for sources, objects, and opts %% when removing deprecated options. -record(spec, {type::'drv' | 'exe', target::file:filename(), sources = [] :: [file:filename(), ...] | [], objects = [] :: [file:filename(), ...] | [], opts = [] ::list() | []}). compile(Config, AppFile) -> rebar_utils:deprecated(port_sources, port_specs, Config, "soon"), rebar_utils:deprecated(so_name, port_specs, Config, "soon"), rebar_utils:deprecated(so_specs, port_specs, Config, "soon"), %% TODO: remove SpecType and OldSources make get_specs/2 %% return list(#spec{}) when removing deprecated options {SpecType, {OldSources, Specs}} = get_specs(Config, AppFile), case {SpecType, OldSources, Specs} of {old, [], _} -> ok; % old specs empty {new, [], []} -> ok; % port_specs empty _ -> % have old/new specs SharedEnv = rebar_config:get_env(Config, ?MODULE), %% Compile each of the sources NewBins = compile_sources(OldSources, Specs, SharedEnv), %% Make sure that the target directories exist ?INFO("Using specs ~p\n", [Specs]), lists:foreach(fun(#spec{target=Target}) -> ok = filelib:ensure_dir(Target) end, Specs), %% Only relink if necessary, given the Target %% and list of new binaries lists:foreach( fun(#spec{target=Target, objects=Bins, opts=Opts}) -> AllBins = [sets:from_list(Bins), sets:from_list(NewBins)], Intersection = sets:intersection(AllBins), case needs_link(Target, sets:to_list(Intersection)) of true -> LinkTemplate = select_link_template(Target), Env = proplists:get_value(env, Opts, SharedEnv), Cmd = expand_command(LinkTemplate, Env, string:join(Bins, " "), Target), rebar_utils:sh(Cmd, [{env, Env}]); false -> ?INFO("Skipping relink of ~s\n", [Target]), ok end end, Specs) end. clean(Config, AppFile) -> %% TODO: remove SpecType and OldSources make get_specs/2 %% return list(#spec{}) when removing deprecated options {SpecType, {OldSources, Specs}} = get_specs(Config, AppFile), case {SpecType, OldSources, Specs} of {old, [], _} -> ok; % old specs empty {new, [], []} -> ok; % port_specs empty _ -> % have old/new specs lists:foreach(fun(#spec{target=Target, objects=Objects}) -> rebar_file_utils:delete_each([Target]), rebar_file_utils:delete_each(Objects) end, Specs) end. setup_env(Config) -> setup_env(Config, []). %% =================================================================== %% Internal functions %% =================================================================== setup_env(Config, ExtraEnv) -> %% Extract environment values from the config (if specified) and %% merge with the default for this operating system. This enables %% max flexibility for users. DefaultEnv = filter_env(default_env(), []), PortEnv = filter_env(port_env(Config), []), OverrideEnv = global_defines() ++ PortEnv ++ filter_env(ExtraEnv, []), RawEnv = apply_defaults(os_env(), DefaultEnv) ++ OverrideEnv, expand_vars_loop(merge_each_var(RawEnv, [])). global_defines() -> Defines = rebar_config:get_global(defines, []), Flags = string:join(["-D" ++ D || D <- Defines], " "), [{"ERL_CFLAGS", "$ERL_CFLAGS " ++ Flags}]. replace_extension(File, NewExt) -> OldExt = filename:extension(File), replace_extension(File, OldExt, NewExt). replace_extension(File, OldExt, NewExt) -> filename:rootname(File, OldExt) ++ NewExt. %% %% == compile and link == %% compile_sources([], Specs, SharedEnv) -> % port_spec lists:foldl( fun(#spec{sources=Sources, type=Type, opts=Opts}, NewBins) -> Env = proplists:get_value(env, Opts, SharedEnv), compile_each(Sources, Type, Env, NewBins) end, [], Specs); compile_sources(OldSources, _Specs, SharedEnv) -> % deprecated compile_each(OldSources, drv, SharedEnv, []). compile_each([], _Type, _Env, NewBins) -> lists:reverse(NewBins); compile_each([Source | Rest], Type, Env, NewBins) -> Ext = filename:extension(Source), Bin = replace_extension(Source, Ext, ".o"), case needs_compile(Source, Bin) of true -> ?CONSOLE("Compiling ~s\n", [Source]), Template = select_compile_template(Type, compiler(Ext)), rebar_utils:sh(expand_command(Template, Env, Source, Bin), [{env, Env}]), compile_each(Rest, Type, Env, [Bin | NewBins]); false -> ?INFO("Skipping ~s\n", [Source]), compile_each(Rest, Type, Env, NewBins) end. needs_compile(Source, Bin) -> %% TODO: Generate depends using gcc -MM so we can also %% check for include changes filelib:last_modified(Bin) < filelib:last_modified(Source). needs_link(SoName, []) -> filelib:last_modified(SoName) == 0; needs_link(SoName, NewBins) -> MaxLastMod = lists:max([filelib:last_modified(B) || B <- NewBins]), case filelib:last_modified(SoName) of 0 -> ?DEBUG("Last mod is 0 on ~s\n", [SoName]), true; Other -> ?DEBUG("Checking ~p >= ~p\n", [MaxLastMod, Other]), MaxLastMod >= Other end. %% %% == port_specs == %% get_specs(Config, AppFile) -> case rebar_config:get_local(Config, port_specs, undefined) of undefined -> %% TODO: DEPRECATED: remove support for non-port_specs syntax {old, old_get_specs(Config, AppFile)}; PortSpecs -> {new, get_port_specs(Config, PortSpecs)} end. get_port_specs(Config, PortSpecs) -> Filtered = filter_port_specs(PortSpecs), OsType = os:type(), {[], [get_port_spec(Config, OsType, Spec) || Spec <- Filtered]}. filter_port_specs(Specs) -> [S || S <- Specs, filter_port_spec(S)]. filter_port_spec({ArchRegex, _, _, _}) -> rebar_utils:is_arch(ArchRegex); filter_port_spec({ArchRegex, _, _}) -> rebar_utils:is_arch(ArchRegex); filter_port_spec({_, _}) -> true. get_port_spec(Config, OsType, {Target, Sources}) -> get_port_spec(Config, OsType, {undefined, Target, Sources, []}); get_port_spec(Config, OsType, {Arch, Target, Sources}) -> get_port_spec(Config, OsType, {Arch, Target, Sources, []}); get_port_spec(Config, OsType, {_Arch, Target, Sources, Opts}) -> SourceFiles = port_sources(Sources), ObjectFiles = port_objects(SourceFiles), #spec{type=target_type(Target), target=maybe_switch_extension(OsType, Target), sources=SourceFiles, objects=ObjectFiles, opts=port_opts(Config, Opts)}. port_sources(Sources) -> lists:flatmap(fun filelib:wildcard/1, Sources). port_objects(SourceFiles) -> [replace_extension(O, ".o") || O <- SourceFiles]. port_opts(Config, Opts) -> [port_opt(Config, O) || O <- Opts]. port_opt(Config, {env, Env}) -> {env, setup_env(Config, Env)}; port_opt(_Config, Opt) -> Opt. maybe_switch_extension({win32, nt}, Target) -> switch_to_dll_or_exe(Target); maybe_switch_extension(_OsType, Target) -> Target. switch_to_dll_or_exe(Target) -> case filename:extension(Target) of ".so" -> filename:rootname(Target, ".so") ++ ".dll"; [] -> Target ++ ".exe"; _Other -> Target end. %% TODO: DEPRECATED: remove support for non-port_specs syntax [old_*()] old_get_specs(Config, AppFile) -> OsType = os:type(), SourceFiles = old_get_sources(Config), Specs = case rebar_config:get_local(Config, so_specs, undefined) of undefined -> Objects = port_objects(SourceFiles), %% New form of so_specs is not provided. See if the old form %% of {so_name} is available instead Dir = "priv", SoName = case rebar_config:get_local(Config, so_name, undefined) of undefined -> %% Ok, neither old nor new form is %% available. Use the app name and %% generate a sensible default. AppName = rebar_app_utils:app_name(AppFile), DrvName = ?FMT("~s_drv.so", [AppName]), filename:join([Dir, DrvName]); AName -> %% Old form is available -- use it filename:join(Dir, AName) end, [old_get_so_spec({SoName, Objects}, OsType)]; SoSpecs -> [old_get_so_spec(S, OsType) || S <- SoSpecs] end, {SourceFiles, Specs}. old_get_sources(Config) -> RawSources = rebar_config:get_local(Config, port_sources, ["c_src/*.c"]), FilteredSources = old_filter_port_sources(RawSources), old_expand_sources(FilteredSources). old_filter_port_sources(PortSources) -> [S || S <- PortSources, old_is_arch_port_sources(S)]. old_is_arch_port_sources({Arch, _Sources}) -> rebar_utils:is_arch(Arch); old_is_arch_port_sources(_Sources) -> true. old_expand_sources(Sources) -> lists:flatmap(fun filelib:wildcard/1, Sources). old_get_so_spec({Target, Objects}, OsType) -> #spec{type=drv, target=maybe_switch_extension(OsType, Target), sources=[], objects=Objects, opts=[]}. %% %% == port_env == %% %% %% Choose a compiler variable, based on a provided extension %% compiler(".cc") -> "$CXX"; compiler(".cp") -> "$CXX"; compiler(".cxx") -> "$CXX"; compiler(".cpp") -> "$CXX"; compiler(".CPP") -> "$CXX"; compiler(".c++") -> "$CXX"; compiler(".C") -> "$CXX"; compiler(_) -> "$CC". %% %% Given a list of {Key, Value} variables, and another list of default %% {Key, Value} variables, return a merged list where the rule is if the %% default is expandable expand it with the value of the variable list, %% otherwise just return the value of the variable. %% apply_defaults(Vars, Defaults) -> dict:to_list( dict:merge(fun(Key, VarValue, DefaultValue) -> case is_expandable(DefaultValue) of true -> rebar_utils:expand_env_variable(DefaultValue, Key, VarValue); false -> VarValue end end, dict:from_list(Vars), dict:from_list(Defaults))). %% %% Given a list of {Key, Value} environment variables, where Key may be defined %% multiple times, walk the list and expand each self-reference so that we %% end with a list of each variable singly-defined. %% merge_each_var([], Vars) -> Vars; merge_each_var([{Key, Value} | Rest], Vars) -> Evalue = case orddict:find(Key, Vars) of error -> %% Nothing yet defined for this key/value. %% Expand any self-references as blank. rebar_utils:expand_env_variable(Value, Key, ""); {ok, Value0} -> %% Use previous definition in expansion rebar_utils:expand_env_variable(Value, Key, Value0) end, merge_each_var(Rest, orddict:store(Key, Evalue, Vars)). %% %% Give a unique list of {Key, Value} environment variables, expand each one %% for every other key until no further expansions are possible. %% expand_vars_loop(Vars) -> expand_vars_loop(Vars, [], dict:from_list(Vars), 10). expand_vars_loop(_Pending, _Recurse, _Vars, 0) -> ?ABORT("Max. expansion reached for ENV vars!\n", []); expand_vars_loop([], [], Vars, _Count) -> lists:keysort(1, dict:to_list(Vars)); expand_vars_loop([], Recurse, Vars, Count) -> expand_vars_loop(Recurse, [], Vars, Count-1); expand_vars_loop([{K, V} | Rest], Recurse, Vars, Count) -> %% Identify the variables that need expansion in this value ReOpts = [global, {capture, all_but_first, list}], case re:run(V, "\\\${?(\\w+)}?", ReOpts) of {match, Matches} -> %% Identify the unique variables that need to be expanded UniqueMatches = lists:usort([M || [M] <- Matches]), %% For each variable, expand it and return the final %% value. Note that if we have a bunch of unresolvable %% variables, nothing happens and we don't bother %% attempting further expansion case expand_keys_in_value(UniqueMatches, V, Vars) of V -> %% No change after expansion; move along expand_vars_loop(Rest, Recurse, Vars, Count); Expanded -> %% Some expansion occurred; move to next k/v but %% revisit this value in the next loop to check %% for further expansion NewVars = dict:store(K, Expanded, Vars), expand_vars_loop(Rest, [{K, Expanded} | Recurse], NewVars, Count) end; nomatch -> %% No values in this variable need expansion; move along expand_vars_loop(Rest, Recurse, Vars, Count) end. expand_keys_in_value([], Value, _Vars) -> Value; expand_keys_in_value([Key | Rest], Value, Vars) -> NewValue = case dict:find(Key, Vars) of {ok, KValue} -> rebar_utils:expand_env_variable(Value, Key, KValue); error -> Value end, expand_keys_in_value(Rest, NewValue, Vars). expand_command(TmplName, Env, InFiles, OutFile) -> Cmd0 = proplists:get_value(TmplName, Env), Cmd1 = rebar_utils:expand_env_variable(Cmd0, "PORT_IN_FILES", InFiles), Cmd2 = rebar_utils:expand_env_variable(Cmd1, "PORT_OUT_FILE", OutFile), re:replace(Cmd2, "\\\$\\w+|\\\${\\w+}", "", [global, {return, list}]). %% %% Given a string, determine if it is expandable %% is_expandable(InStr) -> case re:run(InStr,"\\\$",[{capture,none}]) of match -> true; nomatch -> false end. port_env(Config) -> %% TODO: remove support for deprecated port_envs option PortEnv = rebar_utils:get_deprecated_list(Config, port_envs, port_env, [], "soon"), %% TODO: remove migration of deprecated port_env DRV_-/EXE_-less vars %% when the deprecation grace period ends WarnAndConvertVar = fun(Var) -> New = "DRV_" ++ Var, rebar_utils:deprecated(Var, New, "soon"), New end, ConvertVar = fun(Var="CXX_TEMPLATE") -> WarnAndConvertVar(Var); (Var="CC_TEMPLATE") -> WarnAndConvertVar(Var); (Var="LINK_TEMPLATE") -> WarnAndConvertVar(Var); (Var) -> Var end, %% Also warn about references to deprecated vars? omitted for %% performance reasons. ReplaceVars = fun(Val) -> re:replace(Val, "\\$(CXX|CC|LINK)(_TEMPLATE)", "DRV_\\1\\2", [{return,list}, global]) end, Convert = fun({ArchRegex, Var, Val}) -> {ArchRegex, ConvertVar(Var), ReplaceVars(Val)}; ({Var, Val}) -> {ConvertVar(Var), ReplaceVars(Val)} end, [Convert(EnvVar) || EnvVar <- PortEnv]. %% %% Filter a list of env vars such that only those which match the provided %% architecture regex (or do not have a regex) are returned. %% filter_env([], Acc) -> lists:reverse(Acc); filter_env([{ArchRegex, Key, Value} | Rest], Acc) -> case rebar_utils:is_arch(ArchRegex) of true -> filter_env(Rest, [{Key, Value} | Acc]); false -> filter_env(Rest, Acc) end; filter_env([{Key, Value} | Rest], Acc) -> filter_env(Rest, [{Key, Value} | Acc]). erts_dir() -> lists:concat([code:root_dir(), "/erts-", erlang:system_info(version)]). os_env() -> Os = [list_to_tuple(re:split(S, "=", [{return, list}, {parts, 2}])) || S <- os:getenv()], %% Drop variables without a name (win32) [T1 || {K, _V} = T1 <- Os, K =/= []]. select_compile_template(drv, Compiler) -> select_compile_drv_template(Compiler); select_compile_template(exe, Compiler) -> select_compile_exe_template(Compiler). select_compile_drv_template("$CC") -> "DRV_CC_TEMPLATE"; select_compile_drv_template("$CXX") -> "DRV_CXX_TEMPLATE". select_compile_exe_template("$CC") -> "EXE_CC_TEMPLATE"; select_compile_exe_template("$CXX") -> "EXE_CXX_TEMPLATE". select_link_template(Target) -> case target_type(Target) of drv -> "DRV_LINK_TEMPLATE"; exe -> "EXE_LINK_TEMPLATE" end. target_type(Target) -> target_type1(filename:extension(Target)). target_type1(".so") -> drv; target_type1(".dll") -> drv; target_type1("") -> exe; target_type1(".exe") -> exe. erl_interface_dir(Subdir) -> case code:lib_dir(erl_interface, Subdir) of {error, bad_name} -> throw({error, {erl_interface,Subdir,"code:lib_dir(erl_interface)" "is unable to find the erl_interface library."}}); Dir -> Dir end. default_env() -> [ {"CC" , "cc"}, {"CXX", "c++"}, {"DRV_CXX_TEMPLATE", "$CXX -c $CXXFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"}, {"DRV_CC_TEMPLATE", "$CC -c $CFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"}, {"DRV_LINK_TEMPLATE", "$CC $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS -o $PORT_OUT_FILE"}, {"EXE_CXX_TEMPLATE", "$CXX -c $CXXFLAGS $EXE_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"}, {"EXE_CC_TEMPLATE", "$CC -c $CFLAGS $EXE_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"}, {"EXE_LINK_TEMPLATE", "$CC $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS -o $PORT_OUT_FILE"}, {"DRV_CFLAGS" , "-g -Wall -fPIC $ERL_CFLAGS"}, {"DRV_LDFLAGS", "-shared $ERL_LDFLAGS"}, {"EXE_CFLAGS" , "-g -Wall -fPIC $ERL_CFLAGS"}, {"EXE_LDFLAGS", "$ERL_LDFLAGS"}, {"ERL_CFLAGS", lists:concat([" -I", erl_interface_dir(include), " -I", filename:join(erts_dir(), "include"), " "])}, {"ERL_EI_LIBDIR", erl_interface_dir(lib)}, {"ERL_LDFLAGS" , " -L$ERL_EI_LIBDIR -lerl_interface -lei"}, {"ERLANG_ARCH" , rebar_utils:wordsize()}, {"ERLANG_TARGET", rebar_utils:get_arch()}, {"darwin", "DRV_LDFLAGS", "-bundle -flat_namespace -undefined suppress $ERL_LDFLAGS"}, %% Solaris specific flags {"solaris.*-64$", "CFLAGS", "-D_REENTRANT -m64 $CFLAGS"}, {"solaris.*-64$", "CXXFLAGS", "-D_REENTRANT -m64 $CXXFLAGS"}, {"solaris.*-64$", "LDFLAGS", "-m64 $LDFLAGS"}, %% OS X Leopard flags for 64-bit {"darwin9.*-64$", "CFLAGS", "-m64 $CFLAGS"}, {"darwin9.*-64$", "CXXFLAGS", "-m64 $CXXFLAGS"}, {"darwin9.*-64$", "LDFLAGS", "-arch x86_64 $LDFLAGS"}, %% OS X Snow Leopard flags for 32-bit {"darwin10.*-32", "CFLAGS", "-m32 $CFLAGS"}, {"darwin10.*-32", "CXXFLAGS", "-m32 $CXXFLAGS"}, {"darwin10.*-32", "LDFLAGS", "-arch i386 $LDFLAGS"}, %% OS X Lion flags for 32-bit {"darwin11.*-32", "CFLAGS", "-m32 $CFLAGS"}, {"darwin11.*-32", "CXXFLAGS", "-m32 $CXXFLAGS"}, {"darwin11.*-32", "LDFLAGS", "-arch i386 $LDFLAGS"} ]. rebar-2.0.0/src/rebar_protobuffs_compiler.erl000066400000000000000000000115641176314626300213550ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_protobuffs_compiler). -export([compile/2, clean/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== compile(_Config, _AppFile) -> case rebar_utils:find_files("src", ".*\\.proto$") of [] -> ok; FoundFiles -> %% Check for protobuffs library -- if it's not present, fail %% since we have.proto files that need building case protobuffs_is_present() of true -> %% Build a list of output files - { Proto, Beam, Hrl } Targets = [{Proto, beam_file(Proto), hrl_file(Proto)} || Proto <- FoundFiles], %% Compile each proto file compile_each(Targets); false -> ?ERROR("Protobuffs library not present in code path!\n", []), ?FAIL end end. clean(_Config, _AppFile) -> %% Get a list of generated .beam and .hrl files and then delete them Protos = rebar_utils:find_files("src", ".*\\.proto$"), BeamFiles = [fq_beam_file(F) || F <- Protos], HrlFiles = [fq_hrl_file(F) || F <- Protos], Targets = BeamFiles ++ HrlFiles, case Targets of [] -> ok; _ -> delete_each(Targets) end. %% =================================================================== %% Internal functions %% =================================================================== protobuffs_is_present() -> code:which(protobuffs_compile) =/= non_existing. beam_file(Proto) -> filename:basename(Proto, ".proto") ++ "_pb.beam". hrl_file(Proto) -> filename:basename(Proto, ".proto") ++ "_pb.hrl". fq_beam_file(Proto) -> filename:join(["ebin", filename:basename(Proto, ".proto") ++ "_pb.beam"]). fq_hrl_file(Proto) -> filename:join(["include", filename:basename(Proto, ".proto") ++ "_pb.hrl"]). needs_compile(Proto, Beam) -> ActualBeam = filename:join(["ebin", filename:basename(Beam)]), filelib:last_modified(ActualBeam) < filelib:last_modified(Proto). compile_each([]) -> ok; compile_each([{Proto, Beam, Hrl} | Rest]) -> case needs_compile(Proto, Beam) of true -> ?CONSOLE("Compiling ~s\n", [Proto]), case protobuffs_compile:scan_file(Proto) of ok -> %% Compilation worked, but we need to move the %% beam and .hrl file into the ebin/ and include/ %% directories respectively %% TODO: Protobuffs really needs to be better about this ok = filelib:ensure_dir(filename:join("ebin","dummy")), ok = rebar_file_utils:mv(Beam, "ebin"), ok = filelib:ensure_dir(filename:join("include", Hrl)), ok = rebar_file_utils:mv(Hrl, "include"), ok; Other -> ?ERROR("Protobuff compile of ~s failed: ~p\n", [Proto, Other]), ?FAIL end; false -> ok end, compile_each(Rest). delete_each([]) -> ok; delete_each([File | Rest]) -> case file:delete(File) of ok -> ok; {error, enoent} -> ok; {error, Reason} -> ?ERROR("Failed to delete ~s: ~p\n", [File, Reason]) end, delete_each(Rest). rebar-2.0.0/src/rebar_rel_utils.erl000066400000000000000000000201011176314626300172570ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_rel_utils). -export([is_rel_dir/0, is_rel_dir/1, get_reltool_release_info/1, get_rel_release_info/1, get_rel_release_info/2, get_rel_apps/1, get_rel_apps/2, get_previous_release_path/0, get_rel_file_path/2, load_config/1, get_sys_tuple/1, get_target_dir/1, get_root_dir/1, get_target_parent_dir/1]). -include("rebar.hrl"). is_rel_dir() -> is_rel_dir(rebar_utils:get_cwd()). is_rel_dir(Dir) -> Fname = filename:join([Dir, "reltool.config"]), Scriptname = Fname ++ ".script", Res = case filelib:is_regular(Scriptname) of true -> {true, Scriptname}; false -> case filelib:is_regular(Fname) of true -> {true, Fname}; false -> false end end, ?DEBUG("is_rel_dir(~s) -> ~p~n", [Dir, Res]), Res. %% Get release name and version from a reltool.config get_reltool_release_info([{sys, Config}| _]) -> {rel, Name, Ver, _} = proplists:lookup(rel, Config), {Name, Ver}; get_reltool_release_info(ReltoolFile) when is_list(ReltoolFile) -> case file:consult(ReltoolFile) of {ok, ReltoolConfig} -> get_reltool_release_info(ReltoolConfig); _ -> ?ABORT("Failed to parse ~s~n", [ReltoolFile]) end. %% Get release name and version from a rel file get_rel_release_info(RelFile) -> case file:consult(RelFile) of {ok, [{release, {Name, Ver}, _, _}]} -> {Name, Ver}; _ -> ?ABORT("Failed to parse ~s~n", [RelFile]) end. %% Get release name and version from a name and a path get_rel_release_info(Name, Path) -> RelPath = get_rel_file_path(Name, Path), get_rel_release_info(RelPath). %% Get list of apps included in a release from a rel file get_rel_apps(RelFile) -> case file:consult(RelFile) of {ok, [{release, _, _, Apps}]} -> make_proplist(Apps, []); _ -> ?ABORT("Failed to parse ~s~n", [RelFile]) end. %% Get list of apps included in a release from a name and a path get_rel_apps(Name, Path) -> RelPath = get_rel_file_path(Name, Path), get_rel_apps(RelPath). %% Get rel file path from name and path get_rel_file_path(Name, Path) -> [RelFile] = filelib:wildcard(filename:join([Path, "releases", "*", Name ++ ".rel"])), [BinDir|_] = re:replace(RelFile, Name ++ "\\.rel", ""), filename:join([binary_to_list(BinDir), Name ++ ".rel"]). %% Get the previous release path from a global variable get_previous_release_path() -> case rebar_config:get_global(previous_release, false) of false -> ?ABORT("previous_release=PATH is required to " "create upgrade package~n", []); OldVerPath -> OldVerPath end. %% %% Load terms from reltool.config %% load_config(ReltoolFile) -> case rebar_config:consult_file(ReltoolFile) of {ok, Terms} -> expand_version(Terms, filename:dirname(ReltoolFile)); Other -> ?ABORT("Failed to load expected config from ~s: ~p\n", [ReltoolFile, Other]) end. %% %% Look for the {sys, [...]} tuple in the reltool.config file. %% Without this present, we can't run reltool. %% get_sys_tuple(ReltoolConfig) -> case lists:keyfind(sys, 1, ReltoolConfig) of {sys, _} = SysTuple -> SysTuple; false -> ?ABORT("Failed to find {sys, [...]} tuple in reltool.config.", []) end. %% %% Look for {target_dir, TargetDir} in the reltool config file; if none is %% found, use the name of the release as the default target directory. %% get_target_dir(ReltoolConfig) -> case rebar_config:get_global(target_dir, undefined) of undefined -> case lists:keyfind(target_dir, 1, ReltoolConfig) of {target_dir, TargetDir} -> filename:absname(TargetDir); false -> {sys, SysInfo} = get_sys_tuple(ReltoolConfig), case lists:keyfind(rel, 1, SysInfo) of {rel, Name, _Vsn, _Apps} -> filename:absname(Name); false -> filename:absname("target") end end; TargetDir -> filename:absname(TargetDir) end. get_target_parent_dir(ReltoolConfig) -> TargetDir = get_target_dir(ReltoolConfig), case lists:reverse(tl(lists:reverse(filename:split(TargetDir)))) of [] -> "."; Components -> filename:join(Components) end. %% %% Look for root_dir in sys tuple and command line; fall back to %% code:root_dir(). %% get_root_dir(ReltoolConfig) -> {sys, SysInfo} = get_sys_tuple(ReltoolConfig), SysRootDirTuple = lists:keyfind(root_dir, 1, SysInfo), CmdRootDir = rebar_config:get_global(root_dir, undefined), case {SysRootDirTuple, CmdRootDir} of %% root_dir in sys typle and no root_dir on cmd-line {{root_dir, SysRootDir}, undefined} -> SysRootDir; %% root_dir in sys typle and also root_dir on cmd-line {{root_dir, SysRootDir}, CmdRootDir} when CmdRootDir =/= undefined -> case string:equal(SysRootDir, CmdRootDir) of true -> ok; false -> ?WARN("overriding reltool.config root_dir with " "different command line root_dir~n", []) end, CmdRootDir; %% no root_dir in sys typle and no root_dir on cmd-line {false, undefined} -> code:root_dir(); %% no root_dir in sys tuple but root_dir on cmd-line {false, CmdRootDir} when CmdRootDir =/= undefined -> CmdRootDir end. %% =================================================================== %% Internal functions %% =================================================================== make_proplist([{_,_}=H|T], Acc) -> make_proplist(T, [H|Acc]); make_proplist([H|T], Acc) -> App = element(1, H), Ver = element(2, H), make_proplist(T, [{App,Ver}|Acc]); make_proplist([], Acc) -> Acc. expand_version(ReltoolConfig, Dir) -> case lists:keyfind(sys, 1, ReltoolConfig) of {sys, Sys} -> ExpandedSys = {sys, [expand_rel_version(Term, Dir) || Term <- Sys]}, lists:keyreplace(sys, 1, ReltoolConfig, ExpandedSys); _ -> ReltoolConfig end. expand_rel_version({rel, Name, Version, Apps}, Dir) -> {rel, Name, rebar_utils:vcs_vsn(Version, Dir), Apps}; expand_rel_version(Other, _Dir) -> Other. rebar-2.0.0/src/rebar_reltool.erl000066400000000000000000000326651176314626300167570ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_reltool). -export([generate/2, overlay/2, clean/2]). -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). %% =================================================================== %% Public API %% =================================================================== generate(Config, ReltoolFile) -> %% Make sure we have decent version of reltool available check_vsn(), %% Load the reltool configuration from the file ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile), Sys = rebar_rel_utils:get_sys_tuple(ReltoolConfig), %% Spin up reltool server and load our config into it {ok, Server} = reltool:start_server([Sys]), %% Do some validation of the reltool configuration; error messages out of %% reltool are still pretty cryptic validate_rel_apps(Server, Sys), %% Finally, run reltool case catch(run_reltool(Server, Config, ReltoolConfig)) of ok -> ok; {error, failed} -> ?FAIL; Other2 -> ?ERROR("Unexpected error: ~p\n", [Other2]), ?FAIL end. overlay(_Config, ReltoolFile) -> %% Load the reltool configuration from the file ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile), process_overlay(ReltoolConfig). clean(_Config, ReltoolFile) -> ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile), TargetDir = rebar_rel_utils:get_target_dir(ReltoolConfig), rebar_file_utils:rm_rf(TargetDir), rebar_file_utils:delete_each(["reltool.spec"]). %% =================================================================== %% Internal functions %% =================================================================== check_vsn() -> case code:lib_dir(reltool) of {error, bad_name} -> ?ABORT("Reltool support requires the reltool application " "to be installed!", []); Path -> ReltoolVsn = filename:basename(Path), case ReltoolVsn < "reltool-0.5.2" of true -> ?ABORT("Reltool support requires at least reltool-0.5.2; " "this VM is using ~s\n", [ReltoolVsn]); false -> ok end end. process_overlay(ReltoolConfig) -> TargetDir = rebar_rel_utils:get_target_dir(ReltoolConfig), {_BootRelName, BootRelVsn} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig), %% Initialize overlay vars with some basics %% (that can get overwritten) OverlayVars0 = dict:from_list([{erts_vsn, "erts-" ++ erlang:system_info(version)}, {rel_vsn, BootRelVsn}, {target_dir, TargetDir}]), %% Load up any variables specified by overlay_vars OverlayVars1 = overlay_vars(OverlayVars0, ReltoolConfig), OverlayVars = rebar_templater:resolve_variables(dict:to_list(OverlayVars1), OverlayVars1), %% Finally, overlay the files specified by the overlay section case lists:keyfind(overlay, 1, ReltoolConfig) of {overlay, Overlay} when is_list(Overlay) -> execute_overlay(Overlay, OverlayVars, rebar_utils:get_cwd(), TargetDir); false -> ?INFO("No {overlay, [...]} found in reltool.config.\n", []); _ -> ?ABORT("{overlay, [...]} entry in reltool.config " "must be a list.\n", []) end. %% %% Look for overlay_vars file reference. If the user provides an overlay_vars on %% the command line (i.e. a global), the terms from that file OVERRIDE the one %% listed in reltool.config. To re-iterate, this means you can specify a %% variable in the file from reltool.config and then override that value by %% providing an additional file on the command-line. %% overlay_vars(Vars0, ReltoolConfig) -> BaseVars = load_vars_file(proplists:get_value(overlay_vars, ReltoolConfig)), OverrideVars = load_vars_file(rebar_config:get_global(overlay_vars, undefined)), M = fun(_Key, _Base, Override) -> Override end, dict:merge(M, dict:merge(M, Vars0, BaseVars), OverrideVars). %% %% If a filename is provided, construct a dict of terms %% load_vars_file(undefined) -> dict:new(); load_vars_file(File) -> case file:consult(File) of {ok, Terms} -> dict:from_list(Terms); {error, Reason} -> ?ABORT("Unable to load overlay_vars from ~s: ~p\n", [File, Reason]) end. validate_rel_apps(ReltoolServer, {sys, ReltoolConfig}) -> case lists:keyfind(rel, 1, ReltoolConfig) of false -> ok; {rel, _Name, _Vsn, Apps} -> %% Identify all the apps that do NOT exist, based on %% what's available from the reltool server Missing = lists:sort( [App || App <- Apps, app_exists(App, ReltoolServer) == false]), case Missing of [] -> ok; _ -> ?ABORT("Apps in {rel, ...} section not found by " "reltool: ~p\n", [Missing]) end; Rel -> %% Invalid release format! ?ABORT("Invalid {rel, ...} section in reltools.config: ~p\n", [Rel]) end. app_exists(App, Server) when is_atom(App) -> case reltool_server:get_app(Server, App) of {ok, _} -> true; _ -> false end; app_exists(AppTuple, Server) when is_tuple(AppTuple) -> app_exists(element(1, AppTuple), Server). run_reltool(Server, _Config, ReltoolConfig) -> case reltool:get_target_spec(Server) of {ok, Spec} -> %% Pull the target dir and make sure it exists TargetDir = rebar_rel_utils:get_target_dir(ReltoolConfig), mk_target_dir(TargetDir), %% Determine the otp root dir to use RootDir = rebar_rel_utils:get_root_dir(ReltoolConfig), %% Dump the spec, if necessary dump_spec(Spec), %% Have reltool actually run case reltool:eval_target_spec(Spec, RootDir, TargetDir) of ok -> ok; {error, Reason} -> ?ABORT("Failed to generate target from spec: ~p\n", [Reason]) end, {BootRelName, BootRelVsn} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig), ok = create_RELEASES(TargetDir, BootRelName, BootRelVsn), process_overlay(ReltoolConfig); {error, Reason} -> ?ABORT("Unable to generate spec: ~s\n", [Reason]) end. mk_target_dir(TargetDir) -> case filelib:ensure_dir(filename:join(TargetDir, "dummy")) of ok -> ok; {error, eexist} -> %% Output directory already exists; if force=1, wipe it out case rebar_config:get_global(force, "0") of "1" -> rebar_file_utils:rm_rf(TargetDir), ok = file:make_dir(TargetDir); _ -> ?ERROR("Release target directory ~p already exists!\n", [TargetDir]), ?FAIL end; {error, Reason} -> ?ERROR("Failed to make target dir ~p: ~s\n", [TargetDir, file:format_error(Reason)]), ?FAIL end. dump_spec(Spec) -> case rebar_config:get_global(dump_spec, "0") of "1" -> SpecBin = list_to_binary(io_lib:print(Spec, 1, 120, -1)), ok = file:write_file("reltool.spec", SpecBin); _ -> ok end. %% TODO: Merge functionality here with rebar_templater execute_overlay([], _Vars, _BaseDir, _TargetDir) -> ok; execute_overlay([{mkdir, Out} | Rest], Vars, BaseDir, TargetDir) -> OutFile = rebar_templater:render( filename:join([TargetDir, Out, "dummy"]), Vars), ok = filelib:ensure_dir(OutFile), ?DEBUG("Created dir ~s\n", [filename:dirname(OutFile)]), execute_overlay(Rest, Vars, BaseDir, TargetDir); execute_overlay([{copy, In} | Rest], _Vars, BaseDir, TargetDir) -> execute_overlay([{copy, In, ""} | Rest], _Vars, BaseDir, TargetDir); execute_overlay([{copy, In, Out} | Rest], Vars, BaseDir, TargetDir) -> InFile = rebar_templater:render(filename:join(BaseDir, In), Vars), OutFile = rebar_templater:render(filename:join(TargetDir, Out), Vars), case filelib:is_dir(InFile) of true -> ok; false -> ok = filelib:ensure_dir(OutFile) end, rebar_file_utils:cp_r([InFile], OutFile), execute_overlay(Rest, Vars, BaseDir, TargetDir); execute_overlay([{template_wildcard, Wildcard, OutDir} | Rest], Vars, BaseDir, TargetDir) -> %% Generate a series of {template, In, Out} instructions from the wildcard %% that will get processed per normal Ifun = fun(F, Acc0) -> [{template, F, filename:join(OutDir, filename:basename(F))} | Acc0] end, NewInstrs = lists:foldl(Ifun, Rest, filelib:wildcard(Wildcard, BaseDir)), case length(NewInstrs) =:= length(Rest) of true -> ?WARN("template_wildcard: ~s did not match any files!\n", [Wildcard]); false -> ok end, ?DEBUG("template_wildcard: ~s expanded to ~p\n", [Wildcard, NewInstrs]), execute_overlay(NewInstrs, Vars, BaseDir, TargetDir); execute_overlay([{template, In, Out} | Rest], Vars, BaseDir, TargetDir) -> InFile = rebar_templater:render(filename:join(BaseDir, In), Vars), {ok, InFileData} = file:read_file(InFile), OutFile = rebar_templater:render(filename:join(TargetDir, Out), Vars), ok = filelib:ensure_dir(OutFile), case file:write_file(OutFile, rebar_templater:render(InFileData, Vars)) of ok -> ok = apply_file_info(InFile, OutFile), ?DEBUG("Templated ~p\n", [OutFile]), execute_overlay(Rest, Vars, BaseDir, TargetDir); {error, Reason} -> ?ABORT("Failed to template ~p: ~p\n", [OutFile, Reason]) end; execute_overlay([{create, Out, Contents} | Rest], Vars, BaseDir, TargetDir) -> OutFile = rebar_templater:render(filename:join(TargetDir, Out), Vars), ok = filelib:ensure_dir(OutFile), case file:write_file(OutFile, Contents) of ok -> ?DEBUG("Created ~p\n", [OutFile]), execute_overlay(Rest, Vars, BaseDir, TargetDir); {error, Reason} -> ?ABORT("Failed to create ~p: ~p\n", [OutFile, Reason]) end; execute_overlay([{replace, Out, Regex, Replacement} | Rest], Vars, BaseDir, TargetDir) -> execute_overlay([{replace, Out, Regex, Replacement, []} | Rest], Vars, BaseDir, TargetDir); execute_overlay([{replace, Out, Regex, Replacement, Opts} | Rest], Vars, BaseDir, TargetDir) -> Filename = rebar_templater:render(filename:join(TargetDir, Out), Vars), {ok, OrigData} = file:read_file(Filename), Data = re:replace(OrigData, Regex, rebar_templater:render(Replacement, Vars), [global, {return, binary}] ++ Opts), case file:write_file(Filename, Data) of ok -> ?DEBUG("Edited ~s: s/~s/~s/\n", [Filename, Regex, Replacement]), execute_overlay(Rest, Vars, BaseDir, TargetDir); {error, Reason} -> ?ABORT("Failed to edit ~p: ~p\n", [Filename, Reason]) end; execute_overlay([Other | _Rest], _Vars, _BaseDir, _TargetDir) -> {error, {unsupported_operation, Other}}. apply_file_info(InFile, OutFile) -> {ok, FileInfo} = file:read_file_info(InFile), ok = file:write_file_info(OutFile, FileInfo). create_RELEASES(TargetDir, RelName, RelVsn) -> ReleasesDir = filename:join(TargetDir, "releases"), case release_handler:create_RELEASES( TargetDir, ReleasesDir, filename:join([ReleasesDir, RelVsn, RelName ++ ".rel"]), filename:join(TargetDir, "lib")) of ok -> ok; {error, Reason} -> ?ABORT("Failed to create RELEASES file: ~p\n", [Reason]) end. rebar-2.0.0/src/rebar_require_vsn.erl000066400000000000000000000051641176314626300176330ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% %% ------------------------------------------------------------------- -module(rebar_require_vsn). -include("rebar.hrl"). -export([compile/2, eunit/2]). %% =================================================================== %% Public API %% =================================================================== compile(Config, _) -> check_versions(Config). eunit(Config, _) -> check_versions(Config). check_versions(Config) -> ErtsRegex = rebar_config:get(Config, require_erts_vsn, ".*"), ReOpts = [{capture, none}], case re:run(erlang:system_info(version), ErtsRegex, ReOpts) of match -> ?DEBUG("Matched required ERTS version: ~s -> ~s\n", [erlang:system_info(version), ErtsRegex]); nomatch -> ?ABORT("ERTS version ~s does not match required regex ~s\n", [erlang:system_info(version), ErtsRegex]) end, OtpRegex = rebar_config:get(Config, require_otp_vsn, ".*"), case re:run(erlang:system_info(otp_release), OtpRegex, ReOpts) of match -> ?DEBUG("Matched required OTP release: ~s -> ~s\n", [erlang:system_info(otp_release), OtpRegex]); nomatch -> ?ABORT("OTP release ~s does not match required regex ~s\n", [erlang:system_info(otp_release), OtpRegex]) end. rebar-2.0.0/src/rebar_subdirs.erl000066400000000000000000000065261176314626300167470ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_subdirs). -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). -export([preprocess/2]). %% =================================================================== %% Public API %% =================================================================== preprocess(Config, _) -> %% Get the list of subdirs specified in the config (if any). Cwd = rebar_utils:get_cwd(), ListSubdirs = rebar_config:get_local(Config, sub_dirs, []), Subdirs0 = lists:flatmap(fun filelib:wildcard/1, ListSubdirs), case {rebar_core:is_skip_dir(Cwd), Subdirs0} of {true, []} -> {ok, []}; {true, _} -> ?WARN("Ignoring sub_dirs for ~s~n", [Cwd]), {ok, []}; {false, _} -> Check = check_loop(Cwd), ok = lists:foreach(Check, Subdirs0), Subdirs = [filename:join(Cwd, Dir) || Dir <- Subdirs0], {ok, Subdirs} end. %% =================================================================== %% Internal functions %% =================================================================== check_loop(Cwd) -> RebarConfig = filename:join(Cwd, "rebar.config"), fun(Dir0) -> IsSymlink = case file:read_link_info(Dir0) of {ok, #file_info{type=symlink}} -> {true, resolve_symlink(Dir0)}; _ -> {false, Dir0} end, case IsSymlink of {false, Dir="."} -> ?ERROR("infinite loop detected:~nsub_dirs" " entry ~p in ~s~n", [Dir, RebarConfig]); {true, Cwd} -> ?ERROR("infinite loop detected:~nsub_dirs" " entry ~p in ~s is a symlink to \".\"~n", [Dir0, RebarConfig]); _ -> ok end end. resolve_symlink(Dir0) -> {ok, Dir} = file:read_link(Dir0), Dir. rebar-2.0.0/src/rebar_templater.erl000066400000000000000000000365621176314626300172740ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_templater). -export(['create-app'/2, 'create-node'/2, 'list-templates'/2, create/2]). %% API for other utilities that need templating functionality -export([resolve_variables/2, render/2]). -include("rebar.hrl"). -define(TEMPLATE_RE, ".*\\.template\$"). %% =================================================================== %% Public API %% =================================================================== 'create-app'(Config, File) -> %% Alias for create w/ template=simpleapp rebar_config:set_global(template, "simpleapp"), create(Config, File). 'create-node'(Config, File) -> %% Alias for create w/ template=simplenode rebar_config:set_global(template, "simplenode"), create(Config, File). 'list-templates'(_Config, _File) -> %% Load a list of all the files in the escript -- cache it in the pdict %% since we'll potentially need to walk it several times over the course %% of a run. cache_escript_files(), %% Build a list of available templates AvailTemplates = find_disk_templates() ++ find_escript_templates(), ?CONSOLE("Available templates:\n", []), _ = [begin BaseName = filename:basename(F, ".template"), ?CONSOLE("\t* ~s: ~s (~p)\n", [BaseName, F, Type]) end || {Type, F} <- AvailTemplates], ok. create(_Config, _) -> %% Load a list of all the files in the escript -- cache it in the pdict %% since we'll potentially need to walk it several times over the course %% of a run. cache_escript_files(), %% Build a list of available templates AvailTemplates = find_disk_templates() ++ find_escript_templates(), ?DEBUG("Available templates: ~p\n", [AvailTemplates]), TemplateId = template_id(), %% Using the specified template id, find the matching template file/type. %% Note that if you define the same template in both ~/.rebar/templates %% that is also present in the escript, the one on the file system will %% be preferred. {Type, Template} = select_template(AvailTemplates, TemplateId), %% Load the template definition as is and get the list of variables the %% template requires. TemplateTerms = consult(load_file(Type, Template)), case lists:keyfind(variables, 1, TemplateTerms) of {variables, Vars} -> case parse_vars(Vars, dict:new()) of {error, Entry} -> Context0 = undefined, ?ABORT("Failed while processing variables from template ~p." "Variable definitions must follow form of " "[{atom(), term()}]. Failed at: ~p\n", [TemplateId, Entry]); Context0 -> ok end; false -> ?WARN("No variables section found in template ~p; " "using empty context.\n", [TemplateId]), Context0 = dict:new() end, %% Load variables from disk file, if provided Context1 = case rebar_config:get_global(template_vars, undefined) of undefined -> Context0; File -> case file:consult(File) of {ok, Terms} -> %% TODO: Cleanup/merge with similar code in rebar_reltool M = fun(_Key, _Base, Override) -> Override end, dict:merge(M, Context0, dict:from_list(Terms)); {error, Reason} -> ?ABORT("Unable to load template_vars from ~s: ~p\n", [File, Reason]) end end, %% For each variable, see if it's defined in global vars -- if it is, %% prefer that value over the defaults Context2 = update_vars(dict:fetch_keys(Context1), Context1), ?DEBUG("Template ~p context: ~p\n", [TemplateId, dict:to_list(Context1)]), %% Handle variables that possibly include other variables in their %% definition Context = resolve_variables(dict:to_list(Context2), Context2), ?DEBUG("Resolved Template ~p context: ~p\n", [TemplateId, dict:to_list(Context)]), %% Now, use our context to process the template definition -- this %% permits us to use variables within the definition for filenames. FinalTemplate = consult(render(load_file(Type, Template), Context)), ?DEBUG("Final template def ~p: ~p\n", [TemplateId, FinalTemplate]), %% Execute the instructions in the finalized template Force = rebar_config:get_global(force, "0"), execute_template(FinalTemplate, Type, Template, Context, Force, []). %% %% Given a list of key value pairs, for each string value attempt to %% render it using Dict as the context. Storing the result in Dict as Key. %% resolve_variables([], Dict) -> Dict; resolve_variables([{Key, Value0} | Rest], Dict) when is_list(Value0) -> Value = render(list_to_binary(Value0), Dict), resolve_variables(Rest, dict:store(Key, Value, Dict)); resolve_variables([_Pair | Rest], Dict) -> resolve_variables(Rest, Dict). %% %% Render a binary to a string, using mustache and the specified context %% render(Bin, Context) -> %% Be sure to escape any double-quotes before rendering... ReOpts = [global, {return, list}], Str0 = re:replace(Bin, "\\\\", "\\\\\\", ReOpts), Str1 = re:replace(Str0, "\"", "\\\\\"", ReOpts), mustache:render(Str1, Context). %% =================================================================== %% Internal functions %% =================================================================== %% %% Scan the current escript for available files and cache in pdict. %% cache_escript_files() -> {ok, Files} = rebar_utils:escript_foldl( fun(Name, _, GetBin, Acc) -> [{Name, GetBin()} | Acc] end, [], rebar_config:get_global(escript, undefined)), erlang:put(escript_files, Files). template_id() -> case rebar_config:get_global(template, undefined) of undefined -> ?ABORT("No template specified.\n", []); TemplateId -> TemplateId end. find_escript_templates() -> [{escript, Name} || {Name, _Bin} <- erlang:get(escript_files), re:run(Name, ?TEMPLATE_RE, [{capture, none}]) == match]. find_disk_templates() -> OtherTemplates = find_other_templates(), HomeFiles = rebar_utils:find_files(filename:join([os:getenv("HOME"), ".rebar", "templates"]), ?TEMPLATE_RE), LocalFiles = rebar_utils:find_files(".", ?TEMPLATE_RE), [{file, F} || F <- OtherTemplates ++ HomeFiles ++ LocalFiles]. find_other_templates() -> case rebar_config:get_global(template_dir, undefined) of undefined -> []; TemplateDir -> rebar_utils:find_files(TemplateDir, ?TEMPLATE_RE) end. select_template([], Template) -> ?ABORT("Template ~s not found.\n", [Template]); select_template([{Type, Avail} | Rest], Template) -> case filename:basename(Avail, ".template") == Template of true -> {Type, Avail}; false -> select_template(Rest, Template) end. %% %% Read the contents of a file from the appropriate source %% load_file(escript, Name) -> {Name, Bin} = lists:keyfind(Name, 1, erlang:get(escript_files)), Bin; load_file(file, Name) -> {ok, Bin} = file:read_file(Name), Bin. %% %% Parse/validate variables out from the template definition %% parse_vars([], Dict) -> Dict; parse_vars([{Key, Value} | Rest], Dict) when is_atom(Key) -> parse_vars(Rest, dict:store(Key, Value, Dict)); parse_vars([Other | _Rest], _Dict) -> {error, Other}; parse_vars(Other, _Dict) -> {error, Other}. %% %% Given a list of keys in Dict, see if there is a corresponding value defined %% in the global config; if there is, update the key in Dict with it %% update_vars([], Dict) -> Dict; update_vars([Key | Rest], Dict) -> Value = rebar_config:get_global(Key, dict:fetch(Key, Dict)), update_vars(Rest, dict:store(Key, Value, Dict)). %% %% Given a string or binary, parse it into a list of terms, ala file:consult/0 %% consult(Str) when is_list(Str) -> consult([], Str, []); consult(Bin) when is_binary(Bin)-> consult([], binary_to_list(Bin), []). consult(Cont, Str, Acc) -> case erl_scan:tokens(Cont, Str, 0) of {done, Result, Remaining} -> case Result of {ok, Tokens, _} -> {ok, Term} = erl_parse:parse_term(Tokens), consult([], Remaining, [Term | Acc]); {eof, _Other} -> lists:reverse(Acc); {error, Info, _} -> {error, Info} end; {more, Cont1} -> consult(Cont1, eof, Acc) end. write_file(Output, Data, Force) -> %% determine if the target file already exists FileExists = filelib:is_regular(Output), %% perform the function if we're allowed, %% otherwise just process the next template case Force =:= "1" orelse FileExists =:= false of true -> ok = filelib:ensure_dir(Output), case {Force, FileExists} of {"1", true} -> ?CONSOLE("Writing ~s (forcibly overwriting)~n", [Output]); _ -> ?CONSOLE("Writing ~s~n", [Output]) end, case file:write_file(Output, Data) of ok -> ok; {error, Reason} -> ?ABORT("Failed to write output file ~p: ~p\n", [Output, Reason]) end; false -> {error, exists} end. %% %% Execute each instruction in a template definition file. %% execute_template([], _TemplateType, _TemplateName, _Context, _Force, ExistingFiles) -> case ExistingFiles of [] -> ok; _ -> Msg = lists:flatten([io_lib:format("\t* ~p~n", [F]) || F <- lists:reverse(ExistingFiles)]), Help = "To force overwriting, specify force=1 on the command line.\n", ?ERROR("One or more files already exist on disk and " "were not generated:~n~s~s", [Msg , Help]) end; execute_template([{template, Input, Output} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> InputName = filename:join(filename:dirname(TemplateName), Input), case write_file(Output, render(load_file(TemplateType, InputName), Context), Force) of ok -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, exists} -> execute_template(Rest, TemplateType, TemplateName, Context, Force, [Output|ExistingFiles]) end; execute_template([{file, Input, Output} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> InputName = filename:join(filename:dirname(TemplateName), Input), case write_file(Output, load_file(TemplateType, InputName), Force) of ok -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, exists} -> execute_template(Rest, TemplateType, TemplateName, Context, Force, [Output|ExistingFiles]) end; execute_template([{dir, Name} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> case filelib:ensure_dir(filename:join(Name, "dummy")) of ok -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, Reason} -> ?ABORT("Failed while processing template instruction " "{dir, ~s}: ~p\n", [Name, Reason]) end; execute_template([{copy, Input, Output} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> InputName = filename:join(filename:dirname(TemplateName), Input), try rebar_file_utils:cp_r([InputName ++ "/*"], Output) of ok -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles) catch _:_ -> ?ABORT("Failed while processing template instruction " "{copy, ~s, ~s}~n", [Input, Output]) end; execute_template([{chmod, Mod, File} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) when is_integer(Mod) -> case file:change_mode(File, Mod) of ok -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, Reason} -> ?ABORT("Failed while processing template instruction " "{chmod, ~b, ~s}: ~p~n", [Mod, File, Reason]) end; execute_template([{symlink, Existing, New} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> case file:make_symlink(Existing, New) of ok -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, Reason} -> ?ABORT("Failed while processing template instruction " "{symlink, ~s, ~s}: ~p~n", [Existing, New, Reason]) end; execute_template([{variables, _} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); execute_template([Other | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> ?WARN("Skipping unknown template instruction: ~p\n", [Other]), execute_template(Rest, TemplateType, TemplateName, Context, Force, ExistingFiles). rebar-2.0.0/src/rebar_upgrade.erl000066400000000000000000000226271176314626300167230ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2011 Joe Williams (joe@joetify.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_upgrade). -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). -export(['generate-upgrade'/2]). -define(TMP, "_tmp"). %% ==================================================================== %% Public API %% ==================================================================== 'generate-upgrade'(_Config, ReltoolFile) -> %% Get the old release path ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile), TargetParentDir = rebar_rel_utils:get_target_parent_dir(ReltoolConfig), TargetDir = rebar_rel_utils:get_target_dir(ReltoolConfig), OldVerPath = filename:join([TargetParentDir, rebar_rel_utils:get_previous_release_path()]), %% Run checks to make sure that building a package is possible {NewVerPath, NewName, NewVer} = run_checks(OldVerPath, ReltoolConfig), NameVer = NewName ++ "_" ++ NewVer, %% Save the code path prior to doing anything OrigPath = code:get_path(), %% Prepare the environment for building the package ok = setup(OldVerPath, NewVerPath, NewName, NewVer, NameVer), %% Build the package run_systools(NameVer, NewName), %% Boot file changes {ok, _} = boot_files(TargetDir, NewVer, NewName), %% Extract upgrade and tar it back up with changes make_tar(NameVer, NewVer, NewName), %% Clean up files that systools created ok = cleanup(NameVer), %% Restore original path true = code:set_path(OrigPath), ok. %% =================================================================== %% Internal functions %% ================================================================== run_checks(OldVerPath, ReltoolConfig) -> true = rebar_utils:prop_check(filelib:is_dir(OldVerPath), "Release directory doesn't exist (~p)~n", [OldVerPath]), {Name, Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig), NewVerPath = filename:join([rebar_rel_utils:get_target_parent_dir(ReltoolConfig), Name]), true = rebar_utils:prop_check(filelib:is_dir(NewVerPath), "Release directory doesn't exist (~p)~n", [NewVerPath]), {NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NewVerPath), {OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath), true = rebar_utils:prop_check(NewName == OldName, "New and old .rel release names do not match~n", []), true = rebar_utils:prop_check(Name == NewName, "Reltool and .rel release names do not match~n", []), true = rebar_utils:prop_check(NewVer =/= OldVer, "New and old .rel contain the same version~n", []), true = rebar_utils:prop_check(Ver == NewVer, "Reltool and .rel versions do not match~n", []), {NewVerPath, NewName, NewVer}. setup(OldVerPath, NewVerPath, NewName, NewVer, NameVer) -> Src = filename:join([NewVerPath, "releases", NewVer, NewName ++ ".rel"]), Dst = filename:join([".", NameVer ++ ".rel"]), {ok, _} = file:copy(Src, Dst), ok = code:add_pathsa( lists:append([ filelib:wildcard(filename:join([NewVerPath, "lib", "*", "ebin"])), filelib:wildcard(filename:join([OldVerPath, "releases", "*"])), filelib:wildcard(filename:join([OldVerPath, "lib", "*", "ebin"])) ])). run_systools(NewVer, Name) -> Opts = [silent], NameList = [Name], case systools:make_relup(NewVer, NameList, NameList, Opts) of {error, _, Msg} -> ?ABORT("Systools [systools:make_relup/4] aborted with: ~p~n", [Msg]); _ -> ?DEBUG("Relup created~n", []), case systools:make_script(NewVer, Opts) of {error, _, Msg1} -> ?ABORT("Systools [systools:make_script/2] " "aborted with: ~p~n", [Msg1]); _ -> ?DEBUG("Script created~n", []), case systools:make_tar(NewVer, Opts) of {error, _, Msg2} -> ?ABORT("Systools [systools:make_tar/2] " "aborted with: ~p~n", [Msg2]); _ -> ?DEBUG("Tarball created~n", []), ok end end end. boot_files(TargetDir, Ver, Name) -> ok = file:make_dir(filename:join([".", ?TMP])), ok = file:make_dir(filename:join([".", ?TMP, "releases"])), ok = file:make_dir(filename:join([".", ?TMP, "releases", Ver])), case os:type() of {win32,_} -> ok; _ -> ok = file:make_symlink( filename:join(["start.boot"]), filename:join([".", ?TMP, "releases", Ver, Name ++ ".boot"])) end, {ok, _} = file:copy( filename:join([TargetDir, "releases", Ver, "start_clean.boot"]), filename:join([".", ?TMP, "releases", Ver, "start_clean.boot"])), {ok, _} = file:copy( filename:join([TargetDir, "releases", Ver, "sys.config"]), filename:join([".", ?TMP, "releases", Ver, "sys.config"])), {ok, _} = file:copy( filename:join([TargetDir, "releases", Ver, "vm.args"]), filename:join([".", ?TMP, "releases", Ver, "vm.args"])). make_tar(NameVer, NewVer, NewName) -> Filename = NameVer ++ ".tar.gz", {ok, Cwd} = file:get_cwd(), Absname = filename:join([Cwd, Filename]), ok = file:set_cwd(?TMP), ok = erl_tar:extract(Absname, [compressed]), ok = file:delete(Absname), case os:type() of {win32,_} -> {ok, _} = file:copy( filename:join([".", "releases", NewVer, "start.boot"]), filename:join([".", "releases", NewVer, NewName ++ ".boot"])), ok; _ -> ok end, {ok, Tar} = erl_tar:open(Absname, [write, compressed]), ok = erl_tar:add(Tar, "lib", []), ok = erl_tar:add(Tar, "releases", []), ok = erl_tar:close(Tar), ok = file:set_cwd(Cwd), ?CONSOLE("~s upgrade package created~n", [NameVer]). cleanup(NameVer) -> ?DEBUG("Removing files needed for building the upgrade~n", []), Files = [ filename:join([".", NameVer ++ ".rel"]), filename:join([".", NameVer ++ ".boot"]), filename:join([".", NameVer ++ ".script"]), filename:join([".", "relup"]) ], lists:foreach(fun(F) -> ok = file:delete(F) end, Files), ok = remove_dir_tree(?TMP). %% adapted from http://www.erlang.org/doc/system_principles/create_target.html remove_dir_tree(Dir) -> remove_all_files(".", [Dir]). remove_all_files(Dir, Files) -> lists:foreach(fun(File) -> FilePath = filename:join([Dir, File]), {ok, FileInfo, Link} = file_info(FilePath), case {Link, FileInfo#file_info.type} of {false, directory} -> {ok, DirFiles} = file:list_dir(FilePath), remove_all_files(FilePath, DirFiles), file:del_dir(FilePath); _ -> file:delete(FilePath) end end, Files). file_info(Path) -> case file:read_file_info(Path) of {ok, Info} -> {ok, Info, false}; {error, enoent} -> {ok, Info} = file:read_link_info(Path), {ok, Info, true}; Error -> Error end. rebar-2.0.0/src/rebar_utils.erl000066400000000000000000000361601176314626300164310ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- -module(rebar_utils). -export([get_cwd/0, is_arch/1, get_arch/0, wordsize/0, sh/2, find_files/2, find_files/3, now_str/0, ensure_dir/1, beam_to_mod/2, beams/1, erl_to_mod/1, abort/2, escript_foldl/3, find_executable/1, prop_check/3, expand_code_path/0, deprecated/3, deprecated/4, expand_env_variable/3, vcs_vsn/2, get_deprecated_global/3, get_deprecated_list/4, get_deprecated_list/5, get_deprecated_local/4, get_deprecated_local/5, delayed_halt/1]). -include("rebar.hrl"). %% ==================================================================== %% Public API %% ==================================================================== get_cwd() -> {ok, Dir} = file:get_cwd(), Dir. is_arch(ArchRegex) -> case re:run(get_arch(), ArchRegex, [{capture, none}]) of match -> true; nomatch -> false end. get_arch() -> Words = wordsize(), erlang:system_info(otp_release) ++ "-" ++ erlang:system_info(system_architecture) ++ "-" ++ Words. wordsize() -> try erlang:system_info({wordsize, external}) of Val -> integer_to_list(8 * Val) catch error:badarg -> integer_to_list(8 * erlang:system_info(wordsize)) end. %% %% Options = [Option] -- defaults to [use_stdout, abort_on_error] %% Option = ErrorOption | OutputOption | {cd, string()} | {env, Env} %% ErrorOption = return_on_error | abort_on_error | {abort_on_error, string()} %% OutputOption = use_stdout | {use_stdout, bool()} %% Env = [{string(), Val}] %% Val = string() | false %% sh(Command0, Options0) -> ?INFO("sh info:\n\tcwd: ~p\n\tcmd: ~s\n", [get_cwd(), Command0]), ?DEBUG("\topts: ~p\n", [Options0]), DefaultOptions = [use_stdout, abort_on_error], Options = [expand_sh_flag(V) || V <- proplists:compact(Options0 ++ DefaultOptions)], ErrorHandler = proplists:get_value(error_handler, Options), OutputHandler = proplists:get_value(output_handler, Options), Command = 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], Port = open_port({spawn, Command}, PortSettings), case sh_loop(Port, OutputHandler, []) of {ok, _Output} = Ok -> Ok; {error, {_Rc, _Output}=Err} -> ErrorHandler(Command, Err) end. find_files(Dir, Regex) -> find_files(Dir, Regex, true). find_files(Dir, Regex, Recursive) -> filelib:fold_files(Dir, Regex, Recursive, fun(F, Acc) -> [F | Acc] end, []). now_str() -> {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:local_time(), lists:flatten(io_lib:format("~4b/~2..0b/~2..0b ~2..0b:~2..0b:~2..0b", [Year, Month, Day, Hour, Minute, Second])). %% TODO: filelib:ensure_dir/1 corrected in R13B04. Remove when we drop %% support for OTP releases older than R13B04. ensure_dir(Path) -> case filelib:ensure_dir(Path) of ok -> ok; {error,eexist} -> ok; Error -> Error end. -spec abort(string(), [term()]) -> no_return(). abort(String, Args) -> ?ERROR(String, Args), delayed_halt(1). %% TODO: Rename emulate_escript_foldl to escript_foldl and remove %% this function when the time is right. escript:foldl/3 was an %% undocumented exported fun and has been removed in R14. escript_foldl(Fun, Acc, File) -> {module, zip} = code:ensure_loaded(zip), case erlang:function_exported(zip, foldl, 3) of true -> emulate_escript_foldl(Fun, Acc, File); false -> escript:foldl(Fun, Acc, File) end. find_executable(Name) -> case os:find_executable(Name) of false -> false; Path -> "\"" ++ filename:nativename(Path) ++ "\"" end. %% Helper function for checking values and aborting when needed prop_check(true, _, _) -> true; prop_check(false, Msg, Args) -> ?ABORT(Msg, Args). %% Convert all the entries in the code path to absolute paths. expand_code_path() -> CodePath = lists:foldl(fun (Path, Acc) -> [filename:absname(Path) | Acc] end, [], code:get_path()), code:set_path(lists:reverse(CodePath)). %% %% Given env. variable FOO we want to expand all references to %% it in InStr. References can have two forms: $FOO and ${FOO} %% The end of form $FOO is delimited with whitespace or eol %% expand_env_variable(InStr, VarName, RawVarValue) -> case string:chr(InStr, $$) of 0 -> %% No variables to expand InStr; _ -> VarValue = re:replace(RawVarValue, "\\\\", "\\\\\\\\", [global]), %% Use a regex to match/replace: %% Given variable "FOO": match $FOO\s | $FOOeol | ${FOO} RegEx = io_lib:format("\\\$(~s(\\s|$)|{~s})", [VarName, VarName]), ReOpts = [global, {return, list}], re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts) end. vcs_vsn(Vcs, Dir) -> Key = {Vcs, Dir}, try ets:lookup_element(rebar_vsn_cache, Key, 2) catch error:badarg -> VsnString = vcs_vsn_1(Vcs, Dir), ets:insert(rebar_vsn_cache, {Key, VsnString}), VsnString end. vcs_vsn_1(Vcs, Dir) -> case vcs_vsn_cmd(Vcs) of {unknown, VsnString} -> ?DEBUG("vcs_vsn: Unknown VCS atom in vsn field: ~p\n", [Vcs]), VsnString; {cmd, CmdString} -> vcs_vsn_invoke(CmdString, Dir); Cmd -> %% If there is a valid VCS directory in the application directory, %% use that version info Extension = lists:concat([".", Vcs]), case filelib:is_dir(filename:join(Dir, Extension)) of true -> ?DEBUG("vcs_vsn: Primary vcs used for ~s\n", [Dir]), vcs_vsn_invoke(Cmd, Dir); false -> %% No VCS directory found for the app. Depending on source %% tree structure, there may be one higher up, but that can %% yield unexpected results when used with deps. So, we %% fallback to searching for a priv/vsn.Vcs file. VsnFile = filename:join([Dir, "priv", "vsn" ++ Extension]), case file:read_file(VsnFile) of {ok, VsnBin} -> ?DEBUG("vcs_vsn: Read ~s from priv/vsn.~p\n", [VsnBin, Vcs]), string:strip(binary_to_list(VsnBin), right, $\n); {error, enoent} -> ?DEBUG("vcs_vsn: Fallback to vcs for ~s\n", [Dir]), vcs_vsn_invoke(Cmd, Dir) end end end. get_deprecated_global(OldOpt, NewOpt, When) -> get_deprecated_global(OldOpt, NewOpt, undefined, When). get_deprecated_global(OldOpt, NewOpt, Default, When) -> case rebar_config:get_global(NewOpt, Default) of undefined -> case rebar_config:get_global(OldOpt, Default) of undefined -> undefined; Old -> deprecated(OldOpt, NewOpt, When), Old end; New -> New end. get_deprecated_list(Config, OldOpt, NewOpt, When) -> get_deprecated_list(Config, OldOpt, NewOpt, undefined, When). get_deprecated_list(Config, OldOpt, NewOpt, Default, When) -> get_deprecated_3(fun rebar_config:get_list/3, Config, OldOpt, NewOpt, Default, When). get_deprecated_local(Config, OldOpt, NewOpt, When) -> get_deprecated_local(Config, OldOpt, NewOpt, undefined, When). get_deprecated_local(Config, OldOpt, NewOpt, Default, When) -> get_deprecated_3(fun rebar_config:get_local/3, Config, OldOpt, NewOpt, Default, When). deprecated(Old, New, Opts, When) when is_list(Opts) -> case lists:member(Old, Opts) of true -> deprecated(Old, New, When); false -> ok end; deprecated(Old, New, Config, When) -> case rebar_config:get(Config, Old, undefined) of undefined -> ok; _ -> deprecated(Old, New, When) end. deprecated(Old, New, When) -> io:format( <<"WARNING: deprecated ~p option used~n" "Option '~p' has been deprecated~n" "in favor of '~p'.~n" "'~p' will be removed ~s.~n~n">>, [Old, Old, New, Old, When]). -spec delayed_halt(integer()) -> no_return(). delayed_halt(Code) -> %% Work around buffer flushing issue in erlang:halt if OTP older %% than R15B01. %% TODO: remove workaround once we require R15B01 or newer %% R15B01 introduced erlang:halt/2 case erlang:is_builtin(erlang, halt, 2) of true -> halt(Code); false -> case os:type() of {win32, nt} -> timer:sleep(100), halt(Code); _ -> halt(Code), %% workaround to delay exit until all output is written receive after infinity -> ok end end end. %% ==================================================================== %% Internal functions %% ==================================================================== get_deprecated_3(Get, Config, OldOpt, NewOpt, Default, When) -> case Get(Config, NewOpt, Default) of Default -> case Get(Config, OldOpt, Default) of Default -> Default; Old -> deprecated(OldOpt, NewOpt, When), Old end; New -> New end. %% We do the shell variable substitution ourselves on Windows and hope that the %% command doesn't use any other shell magic. patch_on_windows(Cmd, Env) -> case os:type() of {win32,nt} -> "cmd /q /c " ++ lists:foldl(fun({Key, Value}, Acc) -> expand_env_variable(Acc, Key, Value) end, Cmd, Env); _ -> Cmd end. expand_sh_flag(return_on_error) -> {error_handler, fun(_Command, Err) -> {error, Err} end}; expand_sh_flag({abort_on_error, Message}) -> {error_handler, log_msg_and_abort(Message)}; expand_sh_flag(abort_on_error) -> {error_handler, fun log_and_abort/2}; expand_sh_flag(use_stdout) -> {output_handler, fun(Line, Acc) -> ?CONSOLE("~s", [Line]), [Line | Acc] end}; expand_sh_flag({use_stdout, false}) -> {output_handler, fun(Line, Acc) -> [Line | Acc] end}; expand_sh_flag({cd, _CdArg} = Cd) -> {port_settings, Cd}; expand_sh_flag({env, _EnvArg} = Env) -> {port_settings, Env}. -type err_handler() :: fun((string(), {integer(), string()}) -> no_return()). -spec log_msg_and_abort(string()) -> err_handler(). log_msg_and_abort(Message) -> fun(_Command, {_Rc, _Output}) -> ?ABORT(Message, []) end. -spec log_and_abort(string(), {integer(), string()}) -> no_return(). log_and_abort(Command, {Rc, Output}) -> ?ABORT("~s failed with error: ~w and output:~n~s~n", [Command, Rc, Output]). sh_loop(Port, Fun, Acc) -> receive {Port, {data, {eol, Line}}} -> sh_loop(Port, Fun, Fun(Line ++ "\n", Acc)); {Port, {data, {noeol, Line}}} -> sh_loop(Port, Fun, Fun(Line, Acc)); {Port, {exit_status, 0}} -> {ok, lists:flatten(lists:reverse(Acc))}; {Port, {exit_status, Rc}} -> {error, {Rc, lists:flatten(lists:reverse(Acc))}} end. beam_to_mod(Dir, Filename) -> [Dir | Rest] = filename:split(Filename), list_to_atom(filename:basename(string:join(Rest, "."), ".beam")). erl_to_mod(Filename) -> list_to_atom(filename:rootname(filename:basename(Filename))). beams(Dir) -> filelib:fold_files(Dir, ".*\.beam\$", true, fun(F, Acc) -> [F | Acc] end, []). emulate_escript_foldl(Fun, Acc, File) -> case escript:extract(File, [compile_source]) of {ok, [_Shebang, _Comment, _EmuArgs, Body]} -> case Body of {source, BeamCode} -> GetInfo = fun() -> file:read_file_info(File) end, GetBin = fun() -> BeamCode end, {ok, Fun(".", GetInfo, GetBin, Acc)}; {beam, BeamCode} -> GetInfo = fun() -> file:read_file_info(File) end, GetBin = fun() -> BeamCode end, {ok, Fun(".", GetInfo, GetBin, Acc)}; {archive, ArchiveBin} -> zip:foldl(Fun, Acc, {File, ArchiveBin}) end; {error, _} = Error -> Error end. vcs_vsn_cmd(git) -> %% git describe the last commit that touched CWD %% required for correct versioning of apps in subdirs, such as apps/app1 case os:type() of {win32,nt} -> "FOR /F \"usebackq tokens=* delims=\" %i in " "(`git log -n 1 \"--pretty=format:%h\" .`) do " "@git describe --always --tags %i"; _ -> "git describe --always --tags `git log -n 1 --pretty=format:%h .`" end; vcs_vsn_cmd(hg) -> "hg identify -i"; vcs_vsn_cmd(bzr) -> "bzr revno"; vcs_vsn_cmd(svn) -> "svnversion"; vcs_vsn_cmd({cmd, _Cmd}=Custom) -> Custom; vcs_vsn_cmd(Version) -> {unknown, Version}. vcs_vsn_invoke(Cmd, Dir) -> {ok, VsnString} = rebar_utils:sh(Cmd, [{cd, Dir}, {use_stdout, false}]), string:strip(VsnString, right, $\n). rebar-2.0.0/src/rebar_xref.erl000066400000000000000000000166611176314626300162410ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% %% ------------------------------------------------------------------- %% ------------------------------------------------------------------- %% This module borrows heavily from http://github.com/etnt/exrefcheck project as %% written by Torbjorn Tornkvist , Daniel Luna and others. %% ------------------------------------------------------------------- -module(rebar_xref). -include("rebar.hrl"). -export([xref/2]). %% =================================================================== %% Public API %% =================================================================== xref(Config, _) -> %% Spin up xref {ok, _} = xref:start(xref), ok = xref:set_library_path(xref, code_path()), xref:set_default(xref, [{warnings, rebar_config:get(Config, xref_warnings, false)}, {verbose, rebar_config:is_verbose()}]), {ok, _} = xref:add_directory(xref, "ebin"), %% Save the code path prior to doing anything OrigPath = code:get_path(), true = code:add_path(filename:join(rebar_utils:get_cwd(), "ebin")), %% Get list of xref checks we want to run XrefChecks = rebar_config:get(Config, xref_checks, [exports_not_used, undefined_function_calls]), %% Look for exports that are unused by anything ExportsNoWarn = case lists:member(exports_not_used, XrefChecks) of true -> check_exports_not_used(); false -> true end, %% Look for calls to undefined functions UndefNoWarn = case lists:member(undefined_function_calls, XrefChecks) of true -> check_undefined_function_calls(); false -> true end, %% Run custom queries QueryChecks = rebar_config:get(Config, xref_queries, []), QueryNoWarn = lists:all(fun check_query/1, QueryChecks), %% Restore the original code path true = code:set_path(OrigPath), %% Stop xref stopped = xref:stop(xref), case lists:member(false, [ExportsNoWarn, UndefNoWarn, QueryNoWarn]) of true -> ?FAIL; false -> ok end. %% =================================================================== %% Internal functions %% =================================================================== check_exports_not_used() -> {ok, UnusedExports0} = xref:analyze(xref, exports_not_used), UnusedExports = filter_away_ignored(UnusedExports0), %% Report all the unused functions display_mfas(UnusedExports, "is unused export (Xref)"), UnusedExports =:= []. check_undefined_function_calls() -> {ok, UndefinedCalls0} = xref:analyze(xref, undefined_function_calls), UndefinedCalls = [{find_mfa_source(Caller), format_fa(Caller), format_mfa(Target)} || {Caller, Target} <- UndefinedCalls0], lists:foreach( fun({{Source, Line}, FunStr, Target}) -> ?CONSOLE("~s:~w: Warning ~s calls undefined function ~s\n", [Source, Line, FunStr, Target]) end, UndefinedCalls), UndefinedCalls =:= []. check_query({Query, Value}) -> {ok, Answer} = xref:q(xref, Query), case Answer =:= Value of false -> ?CONSOLE("Query ~s~n answer ~p~n did not match ~p~n", [Query, Answer, Value]), false; _ -> true end. code_path() -> [P || P <- code:get_path(), filelib:is_dir(P)] ++ [filename:join(rebar_utils:get_cwd(), "ebin")]. %% %% Ignore behaviour functions, and explicitly marked functions %% filter_away_ignored(UnusedExports) -> %% Functions can be ignored by using %% -ignore_xref([{F, A}, ...]). %% Setup a filter function that builds a list of behaviour callbacks and/or %% any functions marked to ignore. We then use this list to mask any %% functions marked as unused exports by xref F = fun(Mod) -> Attrs = kf(attributes, Mod:module_info()), Ignore = kf(ignore_xref, Attrs), Callbacks = [B:behaviour_info(callbacks) || B <- kf(behaviour, Attrs)], [{Mod, F, A} || {F, A} <- Ignore ++ lists:flatten(Callbacks)] end, AttrIgnore = lists:flatten( lists:map(F, lists:usort([M || {M, _, _} <- UnusedExports]))), [X || X <- UnusedExports, not lists:member(X, AttrIgnore)]. kf(Key, List) -> case lists:keyfind(Key, 1, List) of {Key, Value} -> Value; false -> [] end. display_mfas([], _Message) -> ok; display_mfas([{_Mod, Fun, Args} = MFA | Rest], Message) -> {Source, Line} = find_mfa_source(MFA), ?CONSOLE("~s:~w: Warning: function ~s/~w ~s\n", [Source, Line, Fun, Args, Message]), display_mfas(Rest, Message). format_mfa({M, F, A}) -> ?FMT("~s:~s/~w", [M, F, A]). format_fa({_M, F, A}) -> ?FMT("~s/~w", [F, A]). %% %% Extract an element from a tuple, or undefined if N > tuple size %% safe_element(N, Tuple) -> case catch(element(N, Tuple)) of {'EXIT', {badarg, _}} -> undefined; Value -> Value end. %% %% Given a MFA, find the file and LOC where it's defined. Note that %% xref doesn't work if there is no abstract_code, so we can avoid %% being too paranoid here. %% find_mfa_source({M, F, A}) -> {M, Bin, _} = code:get_object_code(M), AbstractCode = beam_lib:chunks(Bin, [abstract_code]), {ok, {M, [{abstract_code, {raw_abstract_v1, Code}}]}} = AbstractCode, %% Extract the original source filename from the abstract code [{attribute, 1, file, {Source, _}} | _] = Code, %% Extract the line number for a given function def Fn = [E || E <- Code, safe_element(1, E) == function, safe_element(3, E) == F, safe_element(4, E) == A], case Fn of [{function, Line, F, _, _}] -> {Source, Line}; %% do not crash if functions are exported, even though they %% are not in the source. %% parameterized modules add new/1 and instance/1 for example. [] -> {Source, function_not_found} end. rebar-2.0.0/test/000077500000000000000000000000001176314626300135745ustar00rootroot00000000000000rebar-2.0.0/test/rebar_eunit_tests.erl000066400000000000000000000223261176314626300200260ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% @author Chris Bernard %% @doc This tests functionality provided by the rebar command 'eunit'. %% @copyright 2009, 2010 Dave Smith %% ------------------------------------------------------------------- -module(rebar_eunit_tests). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). %% Assuming this test is run inside the rebar 'eunit' %% command, the current working directory will be '.eunit' -define(REBAR_SCRIPT, "../rebar"). -define(TMP_DIR, "tmp_eunit/"). %% ==================================================================== %% Rebar EUnit and Cover Tests %% ==================================================================== eunit_test_() -> {"Ensure EUnit runs with tests in a 'test' dir and no defined suite", setup, fun() -> setup_basic_project(), rebar("-v eunit") end, fun teardown/1, fun(RebarOut) -> [{"Tests in 'test' directory are found and run", ?_assert(string:str(RebarOut, "myapp_mymod_tests:") =/= 0)}, {"Tests in 'src' directory are found and run", ?_assert(string:str(RebarOut, "myapp_mymod:") =/= 0)}, {"Tests are only run once", ?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}] end}. cover_test_() -> {"Ensure Cover runs with tests in a test dir and no defined suite", setup, fun() -> setup_cover_project(), rebar("-v eunit") end, fun teardown/1, [{"All cover reports are generated", assert_files_in("the temporary eunit directory", expected_cover_generated_files())}, {"Only production modules get coverage reports", assert_files_not_in("the temporary eunit directory", [".eunit/myapp_mymod_tests.COVER.html"])}]}. cover_with_suite_test_() -> {"Ensure Cover runs with Tests in a test dir and a test suite", setup, fun() -> setup_cover_project_with_suite(), rebar("-v eunit suite=mysuite") end, fun teardown/1, [{"All cover reports are generated", assert_files_in("the temporary eunit directory", expected_cover_generated_files())}, {"Only production modules get coverage reports", assert_files_not_in("the temporary eunit directory", [".eunit/myapp_mymod_tests.COVER.html", ".eunit/mysuite.COVER.html"])}]}. expected_cover_generated_files() -> [".eunit/index.html", ".eunit/myapp_app.COVER.html", ".eunit/myapp_mymod.COVER.html", ".eunit/myapp_sup.COVER.html"]. cover_coverage_test_() -> {"Coverage is accurately calculated", setup, fun() -> setup_cover_project(), rebar("-v eunit") end, fun teardown/1, [{"Modules that include the EUnit header can still have 100% coverage", %% cover notices the implicit EUnit test/0 func that never gets %% called during eunit:test(TestRepresentation), so NotCounted %% needs to be decremented in this case. assert_full_coverage("myapp_mymod")}]}. %% ==================================================================== %% Environment and Setup Tests %% ==================================================================== environment_test_() -> {"Sanity check the testing environment", setup, fun make_tmp_dir/0, fun remove_tmp_dir/1, [{"Ensure a test project can be created", ?_assert(filelib:is_dir(?TMP_DIR))}, {"Ensure the rebar script can be found, copied, and run", [?_assert(filelib:is_regular(?REBAR_SCRIPT)), fun assert_rebar_runs/0]}]}. assert_rebar_runs() -> prepare_rebar_script(), ?assert(string:str(os:cmd(filename:nativename("./" ++ ?TMP_DIR ++ "rebar")), "No command to run specified!") =/= 0). basic_setup_test_() -> {"Create a simple project with a 'test' directory, a test, and a module", setup, fun setup_basic_project/0, fun teardown/1, %% Test the setup function assert_dirs_in("Basic Project", ["src", "ebin", "test"]) ++ assert_files_in("Basic Project", ["test/myapp_mymod_tests.erl", "src/myapp_mymod.erl"])}. %% ==================================================================== %% Setup and Teardown %% ==================================================================== -define(myapp_mymod, ["-module(myapp_mymod).\n", "-export([myfunc/0]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "myfunc() -> ok.\n", "myprivate_test() -> ?assert(true).\n"]). -define(myapp_mymod_tests, ["-module(myapp_mymod_tests).\n", "-compile([export_all]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "myfunc_test() -> ?assertMatch(ok, myapp_mymod:myfunc()).\n"]). -define(mysuite, ["-module(mysuite).\n", "-export([all_test_/0]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "all_test_() -> [myapp_mymod_defined_in_mysuite_tests].\n"]). -define(myapp_mymod_defined_in_mysuite_tests, ["-module(myapp_mymod_defined_in_mysuite_tests).\n", "-compile([export_all]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "myfunc_test() -> ?assertMatch(ok, myapp_mymod:myfunc()).\n"]). make_tmp_dir() -> ok = file:make_dir(?TMP_DIR). setup_environment() -> ok = make_tmp_dir(), prepare_rebar_script(), ok = file:set_cwd(?TMP_DIR). setup_basic_project() -> setup_environment(), rebar("create-app appid=myapp"), ok = file:make_dir("ebin"), ok = file:make_dir("test"), ok = file:write_file("test/myapp_mymod_tests.erl", ?myapp_mymod_tests), ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod). setup_cover_project() -> setup_basic_project(), ok = file:write_file("rebar.config", "{cover_enabled, true}.\n"). setup_cover_project_with_suite() -> setup_cover_project(), ok = file:write_file("test/mysuite.erl", ?mysuite), ok = file:write_file("test/myapp_mymod_defined_in_mysuite_tests.erl", ?myapp_mymod_defined_in_mysuite_tests). teardown(_) -> ok = file:set_cwd(".."), ok = remove_tmp_dir(). remove_tmp_dir() -> remove_tmp_dir(arg_for_eunit). remove_tmp_dir(_) -> ok = rebar_file_utils:rm_rf(?TMP_DIR). %% ==================================================================== %% Helper Functions %% ==================================================================== prepare_rebar_script() -> Rebar = ?TMP_DIR ++ "rebar", {ok, _} = file:copy(?REBAR_SCRIPT, Rebar), case os:type() of {unix, _} -> [] = os:cmd("chmod u+x " ++ Rebar); {win32, _} -> {ok, _} = file:copy(?REBAR_SCRIPT ++ ".bat", ?TMP_DIR ++ "rebar.bat") end. rebar() -> rebar([]). rebar(Args) when is_list(Args) -> Out = os:cmd(filename:nativename("./rebar") ++ " " ++ Args), %% ?debugMsg("**** Begin"), ?debugMsg(Out), ?debugMsg("**** End"), Out. assert_dirs_in(Name, [Dir|T]) -> [{Name ++ " has directory: " ++ Dir, ?_assert(filelib:is_dir(Dir))} | assert_dirs_in(Name, T)]; assert_dirs_in(_, []) -> []. assert_files_in(Name, [File|T]) -> [{Name ++ " has file: " ++ File, ?_assert(filelib:is_regular(File))} | assert_files_in(Name, T)]; assert_files_in(_, []) -> []. assert_files_not_in(Name, [File|T]) -> [{Name ++ " does not have file: " ++ File, ?_assertNot(filelib:is_regular(File))} | assert_files_not_in(Name, T)]; assert_files_not_in(_, []) -> []. assert_full_coverage(Mod) -> fun() -> {ok, F} = file:read_file(".eunit/index.html"), Result = [X || X <- string:tokens(binary_to_list(F), "\n"), string:str(X, Mod) =/= 0, string:str(X, "100%") =/= 0], ok = file:close(F), ?assert(length(Result) =:= 1) end. rebar-2.0.0/test/rebar_file_utils_tests.erl000066400000000000000000000241151176314626300210370ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com) %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ------------------------------------------------------------------- %% @author Juhani Rankimies %% @doc Tests functionality of rebar_file_utils module. %% @copyright 2009, 2010 Dave Smith %% ------------------------------------------------------------------- -module(rebar_file_utils_tests). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). -define(TMP_DIR, "tmp_file_utils"). -define(DIR_TREE, [{d,"source",[{f,"file1"}, {f,"file2"}]}, {d,"dest",[]}]). -define(FILE_CONTENT, <<"1234567890">>). %% ==================================================================== %% delete_each tests %% ==================================================================== delete_bogus_test_() -> {"delete_each survives nonexisting files", [?_assertMatch(ok, rebar_file_utils:delete_each(["bogus"])), ?_assertMatch(ok, rebar_file_utils:delete_each(["bogus1","bogus2"]))]}. delete_each_test_() -> {"delete_each removes files", setup, fun() -> setup(), rebar_file_utils:delete_each(file_list()) end, fun teardown/1, [assert_files_not_in("source", file_list())]}. %% ==================================================================== %% rm_rf tests %% ==================================================================== rm_rf_wildcard_test_() -> {"rm_rf removes files based on wildcard spec", setup, fun() -> setup(), rebar_file_utils:rm_rf(filename:join([?TMP_DIR,"source","file*"])) end, fun teardown/1, [assert_files_not_in("source", file_list())]}. rm_rf_dir_test_() -> {"rm_rf removes directory tree", setup, fun() -> setup(), rebar_file_utils:rm_rf(filename:join([?TMP_DIR,"source"])) end, fun teardown/1, [?_assertNot(filelib:is_dir(filename:join([?TMP_DIR,"source"])))]}. %% ==================================================================== %% cp_r tests %% ==================================================================== cp_r_file_to_file_test_() -> {"cp_r copies a file to file", setup, fun() -> setup(), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"source","file1"])], filename:join([?TMP_DIR,"dest","new_file"])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,"dest","new_file"])))]}. cp_r_file_to_dir_test_() -> {"cp_r copies a file to directory", setup, fun() -> setup(), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"source","file1"])], filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,"dest","file1"])))]}. cp_r_dir_to_dir_test_() -> {"cp_r copies a directory to directory", setup, fun() -> setup(), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"source"])], filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assert(filelib:is_dir(filename:join([?TMP_DIR,"dest","source"]))), assert_files_in("dest/source", [filename:join([?TMP_DIR,"dest","source",F]) || F <- ["file1","file2"]])]}. cp_r_wildcard_file_to_dir_test_() -> {"cp_r copies wildcard files to directory", setup, fun() -> setup(), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"source","*1"])], filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,"dest","file1"])))]}. cp_r_wildcard_dir_to_dir_test_() -> {"cp_r copies wildcard directory to directory", setup, fun() -> setup(), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"sour*"])], filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assert(filelib:is_dir(filename:join([?TMP_DIR,"dest","source"]))), assert_files_in("dest/source", [filename:join([?TMP_DIR,"dest","source",F]) || F <- ["file1","file2"]])]}. cp_r_overwrite_file_test_() -> {"cp_r overwrites destination file", setup, fun() -> setup(), ok = file:write_file(filename:join([?TMP_DIR,"dest","file1"]), <<"test">>), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"source","file1"])], filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assertMatch({ok,?FILE_CONTENT}, file:read_file( filename:join([?TMP_DIR,"dest","file1"])))]}. cp_r_overwrite_dir_test_() -> {"cp_r overwrites destination file (xcopy case on win32)", setup, fun() -> setup(), ok = file:make_dir(filename:join([?TMP_DIR,"dest","source"])), ok = file:write_file( filename:join([?TMP_DIR,"dest","source","file1"]), <<"test">>), rebar_file_utils:cp_r([filename:join([?TMP_DIR,"source"])], filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assertMatch({ok,?FILE_CONTENT}, file:read_file( filename:join([?TMP_DIR,"dest","source","file1"])))]}. cp_r_overwrite_file_fail_test_() -> {"cp_r fails to fs permission errors (file:copy/2 case on win32)", setup, fun() -> setup(), ok = file:write_file( filename:join([?TMP_DIR,"dest","file1"]),<<"test">>), ok = file:change_mode( filename:join([?TMP_DIR,"dest","file1"]),0) end, fun teardown/1, [?_assertError({badmatch,_}, rebar_file_utils:cp_r( [filename:join([?TMP_DIR,"source","file1"])], filename:join([?TMP_DIR,"dest"])))]}. cp_r_overwrite_dir_fail_test_() -> {"cp_r fails to fs permission error (xcopy case on win32)", setup, fun() -> setup(), ok = file:make_dir( filename:join([?TMP_DIR,"dest","source"])), ok = file:write_file( filename:join([?TMP_DIR,"dest","source","file1"]), <<"test">>), ok = file:change_mode( filename:join([?TMP_DIR,"dest","source","file1"]),0) end, fun teardown/1, [?_assertError({badmatch,_}, rebar_file_utils:cp_r( [filename:join([?TMP_DIR,"source"])], filename:join([?TMP_DIR,"dest"])))]}. mv_file_test_() -> {"move a file to folder", setup, fun() -> setup(), rebar_file_utils:mv(filename:join([?TMP_DIR,"source","file1"]), filename:join([?TMP_DIR,"dest"])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,"dest","file1"]))), ?_assertNot(filelib:is_regular( filename:join([?TMP_DIR,"source","file1"])))]}. %% ==================================================================== %% Utilities %% ==================================================================== file_list() -> [filename:join([?TMP_DIR,"source",F]) || F <- ["file1","file2"]]. %% ==================================================================== %% Setup and Teardown %% ==================================================================== setup() -> file:make_dir(?TMP_DIR), make_dir_tree(?TMP_DIR,?DIR_TREE). make_dir_tree(Parent, [{d,Dir,Contents} | Rest]) -> NewDir = filename:join(Parent,Dir), ok = file:make_dir(NewDir), ok = make_dir_tree(NewDir,Contents), ok = make_dir_tree(Parent,Rest); make_dir_tree(Parent, [{f,File} | Rest]) -> ok = file:write_file(filename:join(Parent,File),?FILE_CONTENT), ok = make_dir_tree(Parent,Rest); make_dir_tree(_,[]) -> ok. teardown(_) -> case os:type() of {unix, _} -> os:cmd("rm -rf " ++ ?TMP_DIR ++ " 2>/dev/null"); {win32, _} -> os:cmd("rmdir /S /Q " ++ filename:nativename(?TMP_DIR)) end. %% ==================================================================== %% Assert helpers %% ==================================================================== assert_files_in(Name, [File|T]) -> [{Name ++ " has file: " ++ File, ?_assert(filelib:is_regular(File))} | assert_files_in(Name, T)]; assert_files_in(_, []) -> []. assert_files_not_in(Name, [File|T]) -> [{Name ++ " does not have file: " ++ File, ?_assertNot(filelib:is_regular(File))} | assert_files_not_in(Name, T)]; assert_files_not_in(_, []) -> []. rebar-2.0.0/test/upgrade_project/000077500000000000000000000000001176314626300167515ustar00rootroot00000000000000rebar-2.0.0/test/upgrade_project/README.md000066400000000000000000000021631176314626300202320ustar00rootroot00000000000000#### Building version 0.1 rebar compile rebar generate mv rel/dummy rel/dummy_0.1 rebar clean # start the release: cd rel/dummy_0.1 bin/dummy console erl> dummy_server:get_state(). erl> dummy_server:set_state(123). erl> dummy_server:get_state(). #### Building version 0.2 # Now, in another terminal we prepare an upgrade.. # change release version numbers from 0.1 to 0.2 in $EDITOR apps/dummy/src/dummy.app.src $EDITOR rel/reltool.config rebar compile rebar generate # previous_release path is relative to your rel directory rebar generate-appups previous_release=dummy_0.1 rebar generate-upgrade previous_release=dummy_0.1 tar -zvtf rel/dummy_0.2.tar.gz #### Deploying with release_handler mv rel/dummy_0.2.tar.gz rel/dummy_0.1/releases/ # Now use release_handler in the running erlang console for the deploy: erl> release_handler:unpack_release("dummy_0.2"). erl> release_handler:install_release("0.2"). erl> release_handler:make_permanent("0.2"). erl> release_handler:which_releases(). erl> dummy_server:get_state(). rebar-2.0.0/test/upgrade_project/apps/000077500000000000000000000000001176314626300177145ustar00rootroot00000000000000rebar-2.0.0/test/upgrade_project/apps/dummy/000077500000000000000000000000001176314626300210475ustar00rootroot00000000000000rebar-2.0.0/test/upgrade_project/apps/dummy/src/000077500000000000000000000000001176314626300216365ustar00rootroot00000000000000rebar-2.0.0/test/upgrade_project/apps/dummy/src/dummy.app.src000066400000000000000000000003011176314626300242530ustar00rootroot00000000000000{application, dummy, [ {description, "a dummy app"}, {vsn, "0.1"}, {registered, [ dummy_app ]}, {mod, {dummy_app, []}}, {applications, [kernel, stdlib, sasl]} ]}. rebar-2.0.0/test/upgrade_project/apps/dummy/src/dummy_app.erl000066400000000000000000000002051176314626300243320ustar00rootroot00000000000000-module(dummy_app). -behaviour(application). -export([start/2, stop/1]). start(_,_) -> dummy_sup:start_link(). stop(_) -> ok. rebar-2.0.0/test/upgrade_project/apps/dummy/src/dummy_server.erl000066400000000000000000000022051176314626300250620ustar00rootroot00000000000000-module(dummy_server). -behaviour(gen_server). -export([start_link/0, set_state/1, get_state/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). set_state(What) -> gen_server:call(?MODULE, {set_state, What}). get_state() -> gen_server:call(?MODULE, get_state). %% init([]) -> say("init, setting state to 0", []), {ok, 0}. handle_call({set_state, NewState}, _From, _State) -> {reply, {ok, NewState}, NewState}; handle_call(get_state, _From, State) -> {reply, State, State}. handle_cast('__not_implemented', State) -> {noreply, State}. handle_info(_Info, State) -> say("info ~p, ~p.", [_Info, State]), {noreply, State}. terminate(_Reason, _State) -> say("terminate ~p, ~p", [_Reason, _State]), ok. code_change(_OldVsn, State, _Extra) -> say("code_change ~p, ~p, ~p", [_OldVsn, State, _Extra]), {ok, State}. %% Internal say(Format, Data) -> io:format("~p:~p: ~s~n", [?MODULE, self(), io_lib:format(Format, Data)]). rebar-2.0.0/test/upgrade_project/apps/dummy/src/dummy_sup.erl000066400000000000000000000005351176314626300243670ustar00rootroot00000000000000-module(dummy_sup). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> Dummy = {dummy_server, {dummy_server, start_link, []}, permanent, 5000, worker, [dummy_server]}, {ok, {{one_for_one, 10, 10}, [Dummy]}}. rebar-2.0.0/test/upgrade_project/rebar.config000066400000000000000000000000551176314626300212330ustar00rootroot00000000000000{sub_dirs, [ "apps/dummy", "rel" ]}. rebar-2.0.0/test/upgrade_project/rel/000077500000000000000000000000001176314626300175335ustar00rootroot00000000000000rebar-2.0.0/test/upgrade_project/rel/files/000077500000000000000000000000001176314626300206355ustar00rootroot00000000000000rebar-2.0.0/test/upgrade_project/rel/files/dummy000077500000000000000000000164341176314626300217260ustar00rootroot00000000000000#!/bin/sh # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd) RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*} RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log # Note the trailing slash on $PIPE_DIR/ PIPE_DIR=/tmp/$RUNNER_BASE_DIR/ RUNNER_USER= # Make sure this script is running as the appropriate user if [ ! -z "$RUNNER_USER" ] && [ `whoami` != "$RUNNER_USER" ]; then exec sudo -u $RUNNER_USER -i $0 $@ fi # Make sure CWD is set to runner base dir cd $RUNNER_BASE_DIR # Make sure log directory exists mkdir -p $RUNNER_LOG_DIR # Identify the script name SCRIPT=`basename $0` # Parse out release and erts info START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } # Use releases/VSN/vm.args if it exists otherwise use etc/vm.args if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" ]; then VMARGS_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" else VMARGS_PATH="$RUNNER_ETC_DIR/vm.args" fi # Use releases/VSN/sys.config if it exists otherwise use etc/app.config if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" ]; then CONFIG_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" else CONFIG_PATH="$RUNNER_ETC_DIR/app.config" fi # Extract the target node name from node.args NAME_ARG=`egrep '^-s?name' $VMARGS_PATH` if [ -z "$NAME_ARG" ]; then echo "vm.args needs to have either -name or -sname parameter." exit 1 fi # Extract the name type and name from the NAME_ARG for REMSH REMSH_TYPE=`echo $NAME_ARG | awk '{print $1}'` REMSH_NAME=`echo $NAME_ARG | awk '{print $2}'` # Note the `date +%s`, used to allow multiple remsh to the same node transparently REMSH_NAME_ARG="$REMSH_TYPE attach`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`" REMSH_REMSH_ARG="-remsh $REMSH_NAME" # Extract the target cookie COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH` if [ -z "$COOKIE_ARG" ]; then echo "vm.args needs to have a -setcookie parameter." exit 1 fi # Add ERTS bin dir to our path ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin # Setup command to control the node NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" # Setup remote shell command to control node REMSH="$ERTS_PATH/erl $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG" # Check the first argument for instructions case "$1" in start) # Make sure there is not already a node running RES=`$NODETOOL ping` if [ "$RES" = "pong" ]; then echo "Node is already running!" exit 1 fi shift # remove $1 RUN_PARAM=$(printf "\'%s\' " "$@") HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start $RUN_PARAM" export HEART_COMMAND mkdir -p $PIPE_DIR $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $RUN_PARAM" 2>&1 ;; stop) # Wait for the node to completely stop... case `uname -s` in Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD) # PID COMMAND PID=`ps ax -o pid= -o command=|\ grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` ;; SunOS) # PID COMMAND PID=`ps -ef -o pid= -o args=|\ grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` ;; CYGWIN*) # UID PID PPID TTY STIME COMMAND PID=`ps -efW|grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $2}'` ;; esac $NODETOOL stop ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi while `kill -0 $PID 2>/dev/null`; do sleep 1 done ;; restart) ## Restart the VM without exiting the process $NODETOOL restart ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; reboot) ## Restart the VM completely (uses heart to restart it) $NODETOOL reboot ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; ping) ## See if the VM is alive $NODETOOL ping ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; attach) # Make sure a node IS running RES=`$NODETOOL ping` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift exec $ERTS_PATH/to_erl $PIPE_DIR ;; remote_console) # Make sure a node IS running RES=`$NODETOOL ping` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift exec $REMSH ;; upgrade) if [ -z "$2" ]; then echo "Missing upgrade package argument" echo "Usage: $SCRIPT upgrade {package base name}" echo "NOTE {package base name} MUST NOT include the .tar.gz suffix" exit 1 fi # Make sure a node IS running RES=`$NODETOOL ping` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi node_name=`echo $NAME_ARG | awk '{print $2}'` erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'` $ERTS_PATH/escript $RUNNER_BASE_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2 ;; console|console_clean) # .boot file typically just $SCRIPT (ie, the app name) # however, for debugging, sometimes start_clean.boot is useful: case "$1" in console) BOOTFILE=$SCRIPT ;; console_clean) BOOTFILE=start_clean ;; esac # Setup beam-required vars ROOTDIR=$RUNNER_BASE_DIR BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR export PROGNAME # Dump environment info for logging purposes echo "Exec: $CMD" -- ${1+"$@"} echo "Root: $ROOTDIR" # Log the startup logger -t "$SCRIPT[$$]" "Starting up" # Start the VM exec $CMD -- ${1+"$@"} ;; foreground) # start up the release in the foreground for use by runit # or other supervision services BOOTFILE=$SCRIPT FOREGROUNDOPTIONS="-noinput +Bd" # Setup beam-required vars ROOTDIR=$RUNNER_BASE_DIR BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\///'` CMD="$BINDIR/erlexec $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR export PROGNAME # Dump environment info for logging purposes echo "Exec: $CMD" -- ${1+"$@"} echo "Root: $ROOTDIR" # Start the VM exec $CMD -- ${1+"$@"} ;; *) echo "Usage: $SCRIPT {start|foreground|stop|restart|reboot|ping|console|console_clean|attach|remote_console|upgrade}" exit 1 ;; esac exit 0 rebar-2.0.0/test/upgrade_project/rel/files/erl000077500000000000000000000021361176314626300213470ustar00rootroot00000000000000#!/bin/sh ## This script replaces the default "erl" in erts-VSN/bin. This is necessary ## as escript depends on erl and in turn, erl depends on having access to a ## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect ## of running escript -- the embedded node bypasses erl and uses erlexec directly ## (as it should). ## ## Note that this script makes the assumption that there is a start_clean.boot ## file available in $ROOTDIR/release/VSN. # Determine the abspath of where this script is executing from. ERTS_BIN_DIR=$(cd ${0%/*} && pwd) # Now determine the root directory -- this script runs from erts-VSN/bin, # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR # path. ROOTDIR=${ERTS_BIN_DIR%/*/*} # Parse out release and erts info START_ERL=`cat $ROOTDIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` CMD="$BINDIR/erlexec" export EMU export ROOTDIR export BINDIR export PROGNAME exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"} rebar-2.0.0/test/upgrade_project/rel/files/nodetool000066400000000000000000000113231176314626300224030ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% nodetool: Helper Script for interacting with live nodes %% %% ------------------------------------------------------------------- main(Args) -> ok = start_epmd(), %% Extract the args {RestArgs, TargetNode} = process_args(Args, [], undefined), %% See if the node is currently running -- if it's not, we'll bail case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of {true, pong} -> ok; {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of ["ping"] -> %% If we got this far, the node already responsed to a ping, so just dump %% a "pong" io:format("pong\n"); ["stop"] -> io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]); ["restart"] -> io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]); ["reboot"] -> io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); ["rpc", Module, Function | RpcArgs] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), [RpcArgs], 60000) of ok -> ok; {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); _ -> halt(1) end; ["rpcterms", Module, Function, ArgsAsString] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), consult(ArgsAsString), 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); Other -> io:format("~p\n", [Other]) end; Other -> io:format("Other: ~p\n", [Other]), io:format("Usage: nodetool {ping|stop|restart|reboot}\n") end, net_kernel:stop(). process_args([], Acc, TargetNode) -> {lists:reverse(Acc), TargetNode}; process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) -> erlang:set_cookie(node(), list_to_atom(Cookie)), process_args(Rest, Acc, TargetNode); process_args(["-name", TargetName | Rest], Acc, _) -> ThisNode = append_node_suffix(TargetName, "_maint_"), {ok, _} = net_kernel:start([ThisNode, longnames]), process_args(Rest, Acc, nodename(TargetName)); process_args(["-sname", TargetName | Rest], Acc, _) -> ThisNode = append_node_suffix(TargetName, "_maint_"), {ok, _} = net_kernel:start([ThisNode, shortnames]), process_args(Rest, Acc, nodename(TargetName)); process_args([Arg | Rest], Acc, Opts) -> process_args(Rest, [Arg | Acc], Opts). start_epmd() -> [] = os:cmd(epmd_path() ++ " -daemon"), ok. epmd_path() -> ErtsBinDir = filename:dirname(escript:script_name()), Name = "epmd", case os:find_executable(Name, ErtsBinDir) of false -> case os:find_executable(Name) of false -> io:format("Could not find epmd.~n"), halt(1); GlobalEpmd -> GlobalEpmd end; Epmd -> Epmd end. nodename(Name) -> case string:tokens(Name, "@") of [_Node, _Host] -> list_to_atom(Name); [Node] -> [_, Host] = string:tokens(atom_to_list(node()), "@"), list_to_atom(lists:concat([Node, "@", Host])) end. append_node_suffix(Name, Suffix) -> case string:tokens(Name, "@") of [Node, Host] -> list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host])); [Node] -> list_to_atom(lists:concat([Node, Suffix, os:getpid()])) end. %% %% Given a string or binary, parse it into a list of terms, ala file:consult/0 %% consult(Str) when is_list(Str) -> consult([], Str, []); consult(Bin) when is_binary(Bin)-> consult([], binary_to_list(Bin), []). consult(Cont, Str, Acc) -> case erl_scan:tokens(Cont, Str, 0) of {done, Result, Remaining} -> case Result of {ok, Tokens, _} -> {ok, Term} = erl_parse:parse_term(Tokens), consult([], Remaining, [Term | Acc]); {eof, _Other} -> lists:reverse(Acc); {error, Info, _} -> {error, Info} end; {more, Cont1} -> consult(Cont1, eof, Acc) end. rebar-2.0.0/test/upgrade_project/rel/files/sys.config000066400000000000000000000005141176314626300226420ustar00rootroot00000000000000[ %% SASL config {sasl, [ {sasl_error_logger, {file, "log/sasl-error.log"}}, {errlog_type, error}, {error_logger_mf_dir, "log/sasl"}, % Log directory {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size {error_logger_mf_maxfiles, 5} % 5 files max ]} ]. rebar-2.0.0/test/upgrade_project/rel/files/vm.args000066400000000000000000000006371176314626300221430ustar00rootroot00000000000000## Name of the node -name dummy@127.0.0.1 ## Cookie for distributed erlang -setcookie dummy ## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive ## (Disabled by default..use with caution!) ##-heart ## Enable kernel poll and a few async threads +K true +A 5 ## Increase number of concurrent ports/sockets -env ERL_MAX_PORTS 4096 ## Tweak GC to run more often -env ERL_FULLSWEEP_AFTER 10 rebar-2.0.0/test/upgrade_project/rel/reltool.config000066400000000000000000000014331176314626300224030ustar00rootroot00000000000000{sys, [ {lib_dirs, ["../apps"]}, {rel, "dummy", "0.1", [ kernel, stdlib, sasl, dummy ]}, {rel, "start_clean", "", [kernel, stdlib]}, {boot_rel, "dummy"}, {profile, embedded}, {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)"]}, {excl_archive_filters, [".*"]}, {app, hipe, [{incl_cond, exclude}]}, {app, dummy, [{incl_cond, include}]} ]}. {overlay, [ {mkdir, "log/sasl"}, {copy, "files/erl", "{{erts_vsn}}/bin/erl"}, {copy, "files/nodetool", "{{erts_vsn}}/bin/nodetool"}, {copy, "files/dummy", "bin/dummy"}, {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"}, {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"} ]}.