pax_global_header00006660000000000000000000000064125410377440014521gustar00rootroot0000000000000052 comment=5dea85db1b697466586877bed133748bd80fa180 rebar-2.6.0/000077500000000000000000000000001254103774400126215ustar00rootroot00000000000000rebar-2.6.0/.gitignore000066400000000000000000000001231254103774400146050ustar00rootroot00000000000000/ebin/*.beam /rebar *~ *.orig .*.swp /rt.work /rebar.cmd /.eunit /deps /.rebar env rebar-2.6.0/.travis.yml000066400000000000000000000003051254103774400147300ustar00rootroot00000000000000sudo: false language: erlang otp_release: - R16B02 - R16B01 - R16B - R15B01 - R15B - R14B04 - R14B03 - 17.0 before_script: - hostname -f - cc -v - ld -v script: "make travis" rebar-2.6.0/CONTRIBUTING.md000066400000000000000000000101641254103774400150540ustar00rootroot00000000000000Contributing to rebar --------------------- Before implementing a new feature, please submit a ticket to discuss your plans. The feature might have been rejected already, or the implementation might already be decided. See [Community and Resources](README.md#community-and-resources). Code style ---------- The following rules must be followed: * Do not introduce trailing whitespace * Do not mix spaces and tabs * Do not introduce lines longer than 80 characters The following rules should be followed: * Write small functions whenever possible * Avoid having too many clauses containing clauses containing clauses. Basically, avoid deeply nested functions. [erlang-mode (emacs)](http://www.erlang.org/doc/man/erlang.el.html) indentation is preferred. This will keep the code base consistent. vi users are encouraged to give [Vim emulation](http://emacswiki.org/emacs/Evil) ([more info](https://gitorious.org/evil/pages/Home)) a try. Pull requests and branching --------------------------- Use one topic branch per pull request. If you do that, you can add extra commits or fix up buggy commits via `git rebase -i`, and update the branch. The updated branch will be visible in the same pull request. Therefore, you should not open a new pull request when you have to fix your changes. Do not commit to master in your fork. Provide a clean branch without merge commits. Tests ----- As a general rule, any behavioral change to rebar requires a test to go with it. If there's already a test case, you may have to modify that one. If there isn't a test case or a test suite, add a new test case or suite in `inttest/`. [retest](https://github.com/dizzyd/retest) based tests are preferred, but we also have EUnit tests in `test/`. Say you've added a new test case in `inttest/erlc`. To only execute the modified suite, you would do the following: ```sh # First we build rebar and its deps to also get `deps/retest/retest` $ make debug deps # Now we can test the modified erlc suite $ deps/retest/retest -v inttest/erlc ``` To test EUnit tests, you would do: ```sh $ make debug $ ./rebar -v eunit ``` You can also run `make test` to execute both EUnit and [retest](https://github.com/dizzyd/retest) tests as `make check` does. Credit ------ To give everyone proper credit in addition to the git history, please feel free to append your name to `THANKS` in your first contribution. Committing your changes ----------------------- Please ensure that all commits pass all tests, and do not have extra Dialyzer warnings. To do that, run `make check`. If you didn't build via `make debug` at first, the beam files in `ebin/` might be missing debug_info required for [xref](http://www.erlang.org/doc/man/xref.html) and [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html), causing a test failure. If that happens, running `make clean` before running `make check` could solve the problem. #### Structuring your commits Fixing a bug is one commit. Adding a feature is one commit. Adding two features is two commits. Two unrelated changes is two commits. If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup commit into the original commit. #### Writing Commit Messages It's important to write a proper commit title and description. The commit title must be at most 50 characters; it is the first line of the commit text. The second line of the commit text must be left blank. The third line and beyond is the commit message. You should write a commit message. If you do, wrap all lines at 72 characters. You should explain what the commit does, what references you used, and any other information that helps understanding your changes. Basically, structure your commit message like this:
One line summary (at most 50 characters)

Longer description (wrap at 72 characters)
##### Commit title/summary * At most 50 characters * What was changed * Imperative present tense (Fix, Add, Change) * `Fix bug 123` * `Add 'foobar' command` * `Change default timeout to 123` * No period ##### Commit description * Wrap at 72 characters * Why, explain intention and implementation approach * Present tense rebar-2.6.0/LICENSE000066400000000000000000000236771254103774400136450ustar00rootroot00000000000000 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.6.0/Makefile000066400000000000000000000015221254103774400142610ustar00rootroot00000000000000.PHONY: clean xref_warnings deps test test_eunit test_inttest REBAR=$(PWD)/rebar RETEST=$(PWD)/deps/retest/retest LOG_LEVEL?=debug RT_TARGETS?=inttest all: ./bootstrap clean: @rm -rf rebar .rebar/erlcinfo ebin/*.beam inttest/rt.work rt.work .eunit distclean: clean @rm -rf deps debug: @./bootstrap debug check: debug xref dialyzer deps test xref: @./rebar xref build_plt: @./rebar build-plt dialyzer: @./rebar dialyze binary: VSN = $(shell ./rebar -V) binary: clean all @cp rebar ../rebar.wiki/rebar (cd ../rebar.wiki && git commit -m "Update $(VSN)" rebar) deps: @REBAR_EXTRA_DEPS=1 $(REBAR) get-deps @(cd deps/retest && $(REBAR) compile escriptize) test: test_eunit test_inttest test_eunit: all @$(REBAR) eunit test_inttest: all deps @$(RETEST) -l $(LOG_LEVEL) $(RT_TARGETS) travis: clean debug xref clean all deps test rebar-2.6.0/README.md000066400000000000000000000052201254103774400140770ustar00rootroot00000000000000rebar ===== rebar is an Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. [![Build Status](https://secure.travis-ci.org/rebar/rebar.png?branch=master)](http://travis-ci.org/rebar/rebar) 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/HOWTO/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/rebar/rebar/wiki/rebar #### Building rebar ```sh $ git clone git://github.com/rebar/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 ===================== Please refer to [CONTRIBUTING](CONTRIBUTING.md). Community and Resources ----------------------- In case of problems that cannot be solved through documentation or examples, you may want to try to contact members of the community for help. The community is also where you want to go for questions about how to extend rebar, fill in bug reports, and so on. The main place to go for questions is the [rebar mailing list](http://lists.basho.com/pipermail/rebar_lists.basho.com/). If you need quick feedback, you can try the #rebar channel on [irc.freenode.net](http://freenode.net). Be sure to check the [wiki](https://github.com/rebar/rebar/wiki) first, just to be sure you're not asking about things with well known answers. For bug reports, roadmaps, and issues, visit the [github issues page](https://github.com/rebar/rebar/issues). General rebar community resources and links: - [Rebar Mailing List](http://lists.basho.com/pipermail/rebar_lists.basho.com/) - #rebar on [irc.freenode.net](http://freenode.net/) - [wiki](https://github.com/rebar/rebar/wiki) - [issues](https://github.com/rebar/rebar/issues) rebar-2.6.0/RELEASE-NOTES.md000066400000000000000000000365141254103774400151220ustar00rootroot00000000000000# 2.6.0 * rebar/203: [Pluggable proto compilers gpb](https://github.com/rebar/rebar/pull/203) * rebar/273: [Use target_dir as source of new version in generate-appups](https://github.com/rebar/rebar/pull/273) * rebar/293: [Check C source dependencies in needs_compile](https://github.com/rebar/rebar/pull/293) * rebar/305: [Fix compiler invocation on multiarch Linux](https://github.com/rebar/rebar/pull/305) * rebar/322: [Treat vsn mismatch as warning if -k/--keep-going](https://github.com/rebar/rebar/pull/322) * rebar/336: [Add details on Dialyzer with "make check"](https://github.com/rebar/rebar/pull/336) * rebar/337: [Implement eflame -p/--profile support](https://github.com/rebar/rebar/pull/337) * rebar/338: [Processing .app.src.script expects a single value to be returned.](https://github.com/rebar/rebar/pull/338) * rebar/344: [Manually clean up paths.](https://github.com/rebar/rebar/pull/344) * rebar/351: [fish shell completions for rebar](https://github.com/rebar/rebar/pull/351) * rebar/352: [Add typer target (rebase of #309)](https://github.com/rebar/rebar/pull/352) * rebar/354: [compiler respects 'keep_going' flag](https://github.com/rebar/rebar/pull/354) * rebar/355: [Fix 'make build_plt'](https://github.com/rebar/rebar/pull/355) * rebar/356: [Fix minor typo in CONTRIBUTING.md](https://github.com/rebar/rebar/pull/356) * rebar/360: [Minor follow-up fixes for #293](https://github.com/rebar/rebar/pull/360) * rebar/368: [Escape more characters in path (fix #367)](https://github.com/rebar/rebar/pull/368) * rebar/371: [Fix cover print truncation when coverage is 100%](https://github.com/rebar/rebar/pull/371) * rebar/372: [Implement eval command via nodetool](https://github.com/rebar/rebar/pull/372) * rebar/376: [Remove check adding ebin to path for edoc target](https://github.com/rebar/rebar/pull/376) * rebar/378: [deps: fix delete-deps if deps_dir ends with dot](https://github.com/rebar/rebar/pull/378) * rebar/382: [Adapt dialyzer_reference to ba466e2d changes](https://github.com/rebar/rebar/pull/382) * rebar/385: [Fix Dialyzer warning introduced in 0caf047f](https://github.com/rebar/rebar/pull/385) * rebar/386: [Recompile .proto files with gpb also with prefix/suffix](https://github.com/rebar/rebar/pull/386) * rebar/386: [Recompile .proto files with gpb also with prefix/suffix](https://github.com/rebar/rebar/pull/386) * rebar/386: [Recompile .proto files with gpb also with prefix/suffix](https://github.com/rebar/rebar/pull/386) * rebar/399: [Increase the timeout for the inttest/proto_gpb](https://github.com/rebar/rebar/pull/399) * rebar/400: [add Emacs/vi header to non-dummy test modules](https://github.com/rebar/rebar/pull/400) * rebar/403: [Update reference to installation notes](https://github.com/rebar/rebar/pull/403) * rebar/404: [Consistently format export attributes](https://github.com/rebar/rebar/pull/404) * rebar/405: [Generate cachegrind file if erlgrind is available](https://github.com/rebar/rebar/pull/405) * rebar/406: [Fix deps path check in rebar_ct:collect_glob/3](https://github.com/rebar/rebar/pull/406) * rebar/408: [Fix/remove duplicate console message](https://github.com/rebar/rebar/pull/408) * rebar/411: [Fix app.config argument passing when using ct_run (take 2)](https://github.com/rebar/rebar/pull/411) * rebar/413: [Revert "Merge pull request #386 ... gpb-recompilation...detection"](https://github.com/rebar/rebar/pull/413) * rebar/418: [Fix #415 (reltool vsn check)](https://github.com/rebar/rebar/pull/418) * rebar/420: [Mock gpb and protobuffs, in inttest, replacing external dependencies](https://github.com/rebar/rebar/pull/420) * rebar/421: [inttest/ct3: fix travis-ci breakage](https://github.com/rebar/rebar/pull/421) * rebar/424: [Gpb recompilation detection (using base compiler)](https://github.com/rebar/rebar/pull/424) * rebar/425: [Copy instead of rsync gpb and protobuffs inttest mocks](https://github.com/rebar/rebar/pull/425) * rebar/426: [Fixed #133. Release upgrade now handle long and short names properly.](https://github.com/rebar/rebar/pull/426) * rebar/428: [Remove a git url in gpb and protobuffs inttest configs](https://github.com/rebar/rebar/pull/428) * rebar/430: [Exit with proper status code if 'eval' fails](https://github.com/rebar/rebar/pull/430) * rebar/432: [Document recursive_cmds in -r help string as well](https://github.com/rebar/rebar/pull/432) * rebar/433: [inttest/ct3: fix overlong line](https://github.com/rebar/rebar/pull/433) * rebar/437: [Fix .app.src.script bug introduced in b44b4f4](https://github.com/rebar/rebar/pull/437) * rebar/440: [Delete obsolete file](https://github.com/rebar/rebar/pull/440) * rebar/442: [.travis.yml: print information before running script](https://github.com/rebar/rebar/pull/442) * rebar/444: [Fix incorrect "not an app dir" warning](https://github.com/rebar/rebar/pull/444) * rebar/445: [rebar doesn't respect the order of erl_first_files given in the rebar.conf file](https://github.com/rebar/rebar/pull/445) * rebar/447: [Support custom protobuf directory](https://github.com/rebar/rebar/pull/447) * rebar/449: [Support .appup.src files](https://github.com/rebar/rebar/pull/449) * rebar/452: [added 'shell' command to bash-completion](https://github.com/rebar/rebar/pull/452) * rebar/456: [Generate json output from cover](https://github.com/rebar/rebar/pull/456) * rebar/458: [Change env var delimiter to match non-word](https://github.com/rebar/rebar/pull/458) * rebar/459: [Remove -m64 flag.](https://github.com/rebar/rebar/pull/459) * rebar/460: [Add Dialyzer plugin](https://github.com/rebar/rebar/pull/460) * rebar/461: [Fix OTP .appup.src processing on empty lists](https://github.com/rebar/rebar/pull/461) * rebar/463: [Print more info when profiling](https://github.com/rebar/rebar/pull/463) * rebar/466: [Improve test targets in Makefile](https://github.com/rebar/rebar/pull/466) * rebar/467: [Refactor logic and optimizations in rebar_erlc_compiler:doterl_compile/4](https://github.com/rebar/rebar/pull/467) * rebar/469: [Fix dialyzer warnings](https://github.com/rebar/rebar/pull/469) * rebar/470: [Fix whitespace errors](https://github.com/rebar/rebar/pull/470) * rebar/471: [Fix whitespace errors](https://github.com/rebar/rebar/pull/471) * rebar/475: [Adapt to 18.x time api changes](https://github.com/rebar/rebar/pull/475) * rebar/477: [Enable parse transformations in rebar config](https://github.com/rebar/rebar/pull/477) * rebar/478: [bootstrap: better warning fix (Thanks James Fish)](https://github.com/rebar/rebar/pull/478) * rebar/482: [Windows runner - CD to node root](https://github.com/rebar/rebar/pull/482) * rebar/484: [/me added to THANKS](https://github.com/rebar/rebar/pull/484) * rebar/485: [avoid pre-compile time errors in expand_include_lib_path](https://github.com/rebar/rebar/pull/485) * rebar/487: [erlc: fix recently introduced whitespace errors](https://github.com/rebar/rebar/pull/487) * rebar/489: [Fix crash on failed build console output](https://github.com/rebar/rebar/pull/489) * rebar/494: [Update line number of allowed dialyzer error](https://github.com/rebar/rebar/pull/494) * rebar/496: [Update PLT detail for make build_plt](https://github.com/rebar/rebar/pull/496) * rebar/499: [Try one more location for the OTP_VERSION file](https://github.com/rebar/rebar/pull/499) * rebar/500: [Makefile: clean only .rebar/erlcinfo](https://github.com/rebar/rebar/pull/500) * rebar/502: [Don't crash if missing OTP_VERSION file (fix #350)](https://github.com/rebar/rebar/pull/502) * rebar/504: [Drop `shared` PLTs support and change PLT name to .plt](https://github.com/rebar/rebar/pull/504) * rebar/505: [rebar_utils: explain pdict use](https://github.com/rebar/rebar/pull/505) * rebar/508: [rebar_core: add missing newline in log msg](https://github.com/rebar/rebar/pull/508) * rebar/509: [erlc: do not crash if dep file cannot be found](https://github.com/rebar/rebar/pull/509) * rebar/510: [Fix 'make deps'](https://github.com/rebar/rebar/pull/510) * rebar/511: [Add and use memoization server](https://github.com/rebar/rebar/pull/511) * rebar/512: [rmemo: properly handle unsupported call](https://github.com/rebar/rebar/pull/512) # Rebar 2.5.1 * rebar/299: [Fix OS X resource fork handling (Reported-by: Richard O'Keefe)](https://github.com/rebar/rebar/pull/299) * rebar/307: [bootstrap now accepts --help usage flag](https://github.com/rebar/rebar/pull/307) * rebar/316: [fix for #314 (rebar shell somehow blocks using io:format in gen_server handle_call)](https://github.com/rebar/rebar/pull/316) * rebar/327: [Adapt arch string to versioning scheme changes (>= 17.x)](https://github.com/rebar/rebar/pull/327) * rebar/328: [Follow-up typo fixes for #327](https://github.com/rebar/rebar/pull/328) * rebar/330: [Remove experimental label from 'eunit tests='](https://github.com/rebar/rebar/pull/330) * rebar/332: [Update dialyzer_reference](https://github.com/rebar/rebar/pull/332) # Rebar 2.5.0 * Reverted rebar/281: [Move include/rebar.hrl to src/rebar.hrl](https://github.com/rebar/rebar/pull/281) as it broke backwards compatibility # Rebar 2.4.0 * rebar/52: [Slim release support](https://github.com/rebar/rebar/pull/52) * rebar/112: [Add code coverage analysis functionality to `qc'](https://github.com/rebar/rebar/pull/112) * rebar/119: [Add qualified name tests specification (see #118)](https://github.com/rebar/rebar/pull/119) * rebar/130: [ct fixes](https://github.com/rebar/rebar/pull/130) * rebar/136: [Add support for the Perforce VCS client via the "p4" tool](https://github.com/rebar/rebar/pull/136) * rebar/195: [Switch template instructions](https://github.com/rebar/rebar/pull/195) * rebar/229: [Add REBAR to environment before executing hooks](https://github.com/rebar/rebar/pull/229) * rebar/260: [Quote include/lib paths to handle spaces in Erlang installs (fixes build on windows)](https://github.com/rebar/rebar/pull/260) * rebar/280: [improve output when using `rebar shell`](https://github.com/rebar/rebar/pull/280) * rebar/281: [Move include/rebar.hrl to src/rebar.hrl](https://github.com/rebar/rebar/pull/281) * rebar/284: [Error 'Command not found' when sname is used](https://github.com/rebar/rebar/pull/284) * rebar/285: [Fix #249 (erlc regression)](https://github.com/rebar/rebar/pull/285) * rebar/288: [Extend and document contributing rules](https://github.com/rebar/rebar/pull/288) * rebar/289: [erlc: fix typo in update_erlcinfo/3 clause that would make the function fail](https://github.com/rebar/rebar/pull/289) * rebar/290: [erlc: replace if expression with case of](https://github.com/rebar/rebar/pull/290) * rebar/292: [Namespaced types: fix build for 17.0](https://github.com/rebar/rebar/pull/292) * rebar/296: [Add gen_event template](https://github.com/rebar/rebar/pull/296) # Rebar 2.3.1 ## PR's Merged * rebar/242: [Extra commits for #129](https://github.com/rebar/rebar/pull/242) * rebar/244: [Document skip_apps=, apps=, and require_*_vsn](https://github.com/rebar/rebar/pull/244) * rebar/251: [Make sure that eunit/qc_compile_opts works](https://github.com/rebar/rebar/pull/251) * rebar/255: [rebar.app: remove superfluous quoting](https://github.com/rebar/rebar/pull/255) * rebar/274: [Use lowercase for Windows drive name to resolve issue #250](https://github.com/rebar/rebar/pull/274) # Rebar 2.3.0 ## PR's Merged * rebar/98: [Repetition of environment variable definitions in child processes (ports)](https://github.com/rebar/rebar/pull/98) * rebar/115: [Incorrect REMSH args when sname is used.](https://github.com/rebar/rebar/pull/115) * rebar/129: [Speed up the compilation process v5](https://github.com/rebar/rebar/pull/129) * rebar/139: [Allow specification of module dependencies for appups](https://github.com/rebar/rebar/pull/139) * rebar/175: [CWD plugins regression](https://github.com/rebar/rebar/pull/175) * rebar/188: [Xref extra path](https://github.com/rebar/rebar/pull/188) * rebar/208: [Fix typo in rebar_erlydtl_compiler](https://github.com/rebar/rebar/pull/208) * rebar/219: [Added R16B01 and R16B02 to travis config.](https://github.com/rebar/rebar/pull/219) * rebar/221: [Adapt erlydtl compiler plugin to latest version of erlydtl](https://github.com/rebar/rebar/pull/221) * rebar/223: [Add random_suite_order option to eunit command](https://github.com/rebar/rebar/pull/223) * rebar/224: [allow suites or tests as options for eunit and ct](https://github.com/rebar/rebar/pull/224) * rebar/230: [eunit: fix dialyzer warnings introduced in 03da5e0b](https://github.com/rebar/rebar/pull/230) * rebar/232: [Document support for abbreviated commands](https://github.com/rebar/rebar/pull/232) * rebar/233: [docs: fix #228](https://github.com/rebar/rebar/pull/233) * rebar/234: [Fix #220 (Reported-by: Joseph Norton)](https://github.com/rebar/rebar/pull/234) * rebar/237: [Add partial support for Erlang/OTP 17](https://github.com/rebar/rebar/pull/237) * rebar/252: [file_utils: properly report errors (fix #95)](https://github.com/rebar/rebar/pull/252) * rebar/254: [Fix 'rebar generate' regression (#253)](https://github.com/rebar/rebar/pull/254) * rebar/265: [Fix 'rebar help clean' function_clause error](https://github.com/rebar/rebar/pull/265) * rebar/268: [Fix #267 (code path regression)](https://github.com/rebar/rebar/pull/268) * rebar/269: [Update THANKS](https://github.com/rebar/rebar/pull/269) # Rebar 2.2.0 ## PR's Merged * rebar/152: [Fix erl_opts use](https://github.com/rebar/rebar/pull/152) * rebar/154: [Fix update-deps with certain forms of the {tag, ...} type](https://github.com/rebar/rebar/pull/154) * rebar/155: [Fixes for #137 and #142](https://github.com/rebar/rebar/pull/155) * rebar/157: [Don't over-aggressively clean the code path in the presence of lib_dir directives](https://github.com/rebar/rebar/pull/157) * rebar/172: [Add missing dep examples and fix existing ones](https://github.com/rebar/rebar/pull/172) * rebar/173: [Fix false reporting of (plain) vsn strings](https://github.com/rebar/rebar/pull/173) * rebar/174: [rebar_core: fix Dialyzer warning introduced in aa46d85 (#157)](https://github.com/rebar/rebar/pull/174) * rebar/177: [Delete unused inttest/retest binary](https://github.com/rebar/rebar/pull/177) * rebar/179: [Make list of commands (for unabbreviation) easier to maintain](https://github.com/rebar/rebar/pull/179) * rebar/183: [generate-upgrade can now take target_dir argument](https://github.com/rebar/rebar/pull/183) * rebar/184: [Fix log levels](https://github.com/rebar/rebar/pull/184) * rebar/185: [Switch retest dep to upstream (dizzyd/retest.git)](https://github.com/rebar/rebar/pull/185) * rebar/189: [inttest/rgen1: increase retest timeout (30s -> 60s)](https://github.com/rebar/rebar/pull/189) * rebar/190: [inttest/rgen_1: double the timeout a second time](https://github.com/rebar/rebar/pull/190) * rebar/191: [Fix #187 (rename getopt and mustache)](https://github.com/rebar/rebar/pull/191) * rebar/196: [Print a more appropriate message on 'rebar info'](https://github.com/rebar/rebar/pull/196) * rebar/198: [Clean up rebar.config.script](https://github.com/rebar/rebar/pull/198) * rebar/199: [rebar_dia_compiler: fix Dialyzer warnings](https://github.com/rebar/rebar/pull/199) * rebar/200: [bootstrap: avoid trying to run 'debug' command](https://github.com/rebar/rebar/pull/200) * rebar/201: [Added a library template.](https://github.com/rebar/rebar/pull/201) * rebar/210: [Fix #205 (erlydtl:compile/3 returns warnings)](https://github.com/rebar/rebar/pull/210) * rebar/212: [Fix basho/rebar#388](https://github.com/rebar/rebar/pull/212) * rebar/214: [Document compile_only=true](https://github.com/rebar/rebar/pull/214) * rebar/215: [Remove experimental flags](https://github.com/rebar/rebar/pull/215) rebar-2.6.0/THANKS000066400000000000000000000041201254103774400135310ustar00rootroot00000000000000The 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 Slava Yurin Phillip Toland Mike Lazar Loic Hoguin Ali Yakout Adam Schepis Amit Kapoor Ulf Wiger Nick Vatamaniuc Daniel Luna Motiejus Jakstys Eric B Merritt Fred Hebert Kresten Krab Thorup David Aaberg Pedram Nimreezi Edwin Fine Lev Walkin Roberto Ostinelli Joe DeVivo Markus Nasman Dmitriy Kargapolov Ryan Zezeski Daniel White Martin Schut Serge Aleynikov Magnus Henoch Artem Teslenko Jeremie Lasalle Ratelle Jose Valim Krzysztof Rutka Mats Cronqvist Matthew Conway Giacomo Olgeni Pedram Nimreezi Sylvain Benner Oliver Ferrigni Dave Thomas Evgeniy Khramtsov YeJun Su Yuki Ito alisdair sullivan Alexander Verbitsky Andras Horvath Drew Varner Roberto Aloi Luis Rascao Vlad Dumitrescu stwind Pavel Baturko Igor Savchuk Mark Anderson Brian H. Ward David Kubecka Carlos Eduardo de Paula Paulo F. Oliveira Derek Brown Danil Onishchenko Stavros Aronis James Fish rebar-2.6.0/bootstrap000077500000000000000000000127631254103774400145750ustar00rootroot00000000000000#!/usr/bin/env escript %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et main(Args) -> case lists:member("--help", Args) of true -> usage(), halt(0); false -> ok end, %% 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", "hg status"}, {git, ".git", "git describe --always --tags", "git status -s"}]), %% Check for force=1 flag to force a rebuild case lists:member("force=1", Args) of true -> rm("ebin/*.beam"); false -> rm("ebin/rebar.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), %% Types dict:dict() and digraph:digraph() have been introduced in %% Erlang 17. %% At the same time, their counterparts dict() and digraph() are to be %% deprecated in Erlang 18. namespaced_types option is used to select %% proper type name depending on the OTP version used. NamespacedTypes = case is_otp(OtpInfo, "^[0-9]+") of true -> {d, namespaced_types}; false -> undefined end, %% Compile all src/*.erl to ebin %% To not accidentally try to compile files like Mac OS X resource forks, %% we only look for rebar source files that start with a letter. case make:files(filelib:wildcard("src/[a-zA-Z]*.erl"), [{outdir, "ebin"}, {i, "include"}, DebugFlag, NamespacedTypes, {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 compile to do proper .app validation etc. %% and rebar escriptize to create the rebar script RebarArgs = Args -- ["debug"], %% Avoid trying to run 'debug' command rebar:main(["compile", "escriptize"] ++ RebarArgs), %% 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"). usage() -> io:format("Usage: bootstrap [OPTION]...~n"), io:format(" force=1 unconditional build~n"), io:format(" debug add debug information~n"). is_otp(OtpInfo, Regex) -> case re:run(OtpInfo, Regex, [{capture, none}]) of match -> true; nomatch -> false end. rm(Path) -> NativePath = filename:nativename(Path), Cmd = case os:type() of {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(rebar_now()), lists:flatten(io_lib:format("~4..0w~2..0w~2..0w_~2..0w~2..0w~2..0w", [Y, M, D, H, Min, S])). rebar_now() -> case erlang:function_exported(erlang, timestamp, 0) of true -> erlang:timestamp(); false -> %% erlang:now/0 was deprecated in 18.0, and as the escript has to %% pass erl_lint:module/1 (even without -mode(compile)), we would %% see a deprecation warning for erlang:now/0. One solution is to %% use -compile({nowarn_deprecated_function, [{erlang, now, 0}]}), %% but that would raise a warning in versions older than 18.0. %% Calling erlang:now/0 via apply/3 avoids that. apply(erlang, now, []) end. vcs_info([]) -> "No VCS info available."; vcs_info([{Id, Dir, VsnCmd, StatusCmd} | Rest]) -> case filelib:is_dir(Dir) of true -> Vsn = string:strip(os:cmd(VsnCmd), both, $\n), Status = case string:strip(os:cmd(StatusCmd), both, $\n) of [] -> ""; _ -> "-dirty" end, lists:concat([Id, " ", Vsn, Status]); 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.6.0/bootstrap.bat000066400000000000000000000000431254103774400153230ustar00rootroot00000000000000@echo off escript.exe bootstrap %* rebar-2.6.0/ebin/000077500000000000000000000000001254103774400135365ustar00rootroot00000000000000rebar-2.6.0/ebin/rebar.app000066400000000000000000000073731254103774400153450ustar00rootroot00000000000000%% -*- 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.6.0"}, {modules, [ rebar, rebar_abnfc_compiler, rebar_app_utils, rebar_appups, rebar_asn1_compiler, rebar_dia_compiler, rebar_base_compiler, rebar_cleaner, rebar_config, rebar_core, rebar_cover_utils, rebar_ct, rebar_deps, rebar_dialyzer, rebar_edoc, rebar_erlc_compiler, rebar_erlydtl_compiler, rebar_escripter, rebar_eunit, rebar_file_utils, rebar_lfe_compiler, rebar_log, rebar_neotoma_compiler, rebar_otp_app, rebar_otp_appup, rebar_port_compiler, rebar_proto_compiler, rebar_protobuffs_compiler, rebar_proto_gpb_compiler, rebar_qc, rebar_rel_utils, rebar_reltool, rebar_require_vsn, rebar_shell, rebar_subdirs, rebar_templater, rebar_upgrade, rebar_utils, rebar_xref, rebar_metacmds, rebar_getopt, rebar_mustache, rmemo ]}, {registered, []}, {applications, [ kernel, stdlib, sasl, compiler, crypto, syntax_tools, tools, eunit, reltool, dialyzer, asn1, snmp, edoc ]}, {env, [ %% Default log level {log_level, warn}, %% 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_proto_compiler, rebar_protobuffs_compiler, rebar_proto_gpb_compiler, rebar_neotoma_compiler, rebar_asn1_compiler, rebar_dia_compiler, rebar_dialyzer, rebar_erlc_compiler, rebar_lfe_compiler, rebar_erlydtl_compiler, rebar_port_compiler, rebar_otp_app, rebar_otp_appup, rebar_ct, rebar_eunit, rebar_qc, rebar_escripter, rebar_edoc, rebar_shell, rebar_xref, rebar_metacmds ]}, {rel_dir, [ rebar_appups, rebar_reltool, rebar_upgrade ]} ]}, {recursive_cmds, [ 'check-deps', compile, 'delete-deps', 'get-deps', 'list-deps', 'update-deps' ]} ]} ]}. rebar-2.6.0/include/000077500000000000000000000000001254103774400142445ustar00rootroot00000000000000rebar-2.6.0/include/rebar.hrl000066400000000000000000000011661254103774400160520ustar00rootroot00000000000000%% TODO: rename FAIL to ABORT once we require at least R13B04 for %% building rebar. Macros with different arity were not supported by the %% compiler before 13B04. -define(FAIL, rebar_utils:abort()). -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(standard_error, error, Str, Args)). -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))). rebar-2.6.0/inttest/000077500000000000000000000000001254103774400143135ustar00rootroot00000000000000rebar-2.6.0/inttest/app_src/000077500000000000000000000000001254103774400157425ustar00rootroot00000000000000rebar-2.6.0/inttest/app_src/app_src.erl000066400000000000000000000000741254103774400200760ustar00rootroot00000000000000-module(app_src). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/app_src/app_src_rt.erl000066400000000000000000000054241254103774400206070ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Vlad Dumitrescu %% %% 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(app_src_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {create, "src/app_src.app.src", app(app_src)}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), LineRegexp = "DEBUG: Consult config file .*/app_src\.app\.src.*", ?assertEqual(true, has_line(Output, LineRegexp)), retest_log:log(debug, "Evaluated .app.src.script~n", []), %% check that ebin/app_src.app exists ?assertMatch(true, filelib:is_regular("ebin/app_src.app")), retest_log:log(debug, "Generated ebin/.app~n", []), %% check that ebin/.app has vsn="2" {ok, Bin} = file:read_file("ebin/app_src.app"), Str = binary_to_list(Bin), ?assertMatch({match, _}, re:run(Str, "{vsn, *\"2\"}")), retest_log:log(debug, "Variable replacement in .app is ok.~n", []), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. %% %% Generate the contents of a simple .app.src file %% app(Name) -> "{application, " ++ atom_to_list(Name) ++ ", [{vsn, \"2\"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}.\n". rebar-2.6.0/inttest/app_src_script/000077500000000000000000000000001254103774400173265ustar00rootroot00000000000000rebar-2.6.0/inttest/app_src_script/app_src_script.erl000066400000000000000000000001031254103774400230370ustar00rootroot00000000000000-module(app_src_script). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/app_src_script/app_src_script_rt.erl000066400000000000000000000060351254103774400235560ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Vlad Dumitrescu %% %% 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(app_src_script_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {create, "src/app_src_script.app.src.script", app_script(app_src_script)}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), Regexp = "DEBUG: Evaluating config script .*/app_src_script\.app\.src\.script.*", ?assertEqual(true, has_line(Output, Regexp)), retest_log:log(debug, "Evaluated .app.src.script~n", []), %% check that ebin/app_src.app exists ?assertMatch(true, filelib:is_regular("ebin/app_src_script.app")), retest_log:log(debug, "Generated ebin/.app~n", []), %% check that ebin/.app has vsn="2" {ok, Bin} = file:read_file("ebin/app_src_script.app"), Str = binary_to_list(Bin), ?assertMatch({match, _}, re:run(Str, "{vsn, *\"2\"}")), retest_log:log(debug, "Variable replacement in .app is ok.~n", []), %% check that ebin/.app doesn't have foo=ok ?assertMatch(nomatch, re:run(Str, "{foo, *ok}")), retest_log:log(debug, "app.src hasn't 'foo' config.~n", []), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. %% %% Generate the contents of a simple .app.src.script file %% app_script(Name) -> "Vsn=\"2\".\n" ++ "{application, " ++ atom_to_list(Name) ++ ", [{vsn, Vsn}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}.\n". rebar-2.6.0/inttest/app_src_script_2/000077500000000000000000000000001254103774400175475ustar00rootroot00000000000000rebar-2.6.0/inttest/app_src_script_2/app_src_script_2.erl000066400000000000000000000001051254103774400235030ustar00rootroot00000000000000-module(app_src_script_2). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/app_src_script_2/app_src_script_2_rt.erl000066400000000000000000000067011254103774400242200ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Vlad Dumitrescu %% %% 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(app_src_script_2_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {create, "src/app_src_script_2.app.src.script", app_script(app_src_script_2)}, {create, "src/app_src_script_2.app.src", app(app_src_script_2)}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), Regexp = "DEBUG: Evaluating config script .*/app_src_script_2\.app\.src\.script.*", ?assertEqual(true, has_line(Output, Regexp)), retest_log:log(debug, "Evaluated .app.src.script~n", []), %% check that ebin/app_src.app exists ?assertMatch(true, filelib:is_regular("ebin/app_src_script_2.app")), retest_log:log(debug, "Generated ebin/.app~n", []), %% check that ebin/.app has vsn="2" (as in .script) {ok, Bin} = file:read_file("ebin/app_src_script_2.app"), Str = binary_to_list(Bin), retest_log:log(debug, "app=~p~n", [Str]), ?assertMatch({match, _}, re:run(Str, "{vsn, *\"2\"}")), retest_log:log(debug, "app.src has version from script.~n", []), %% check that ebin/.app has foo=ok (from .src) ?assertMatch({match, _}, re:run(Str, "{foo, *ok}")), retest_log:log(debug, "app.src has 'foo' config from .src.~n", []), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. %% %% Generate the contents of a simple .app.src.script file %% app_script(Name) -> "[{application," ++ atom_to_list(Name) ++ ",Cfg}] = CONFIG, [{application," ++ atom_to_list(Name) ++ ",lists:keyreplace(vsn, 1, Cfg, {vsn, \"2\"})}].". %% %% Generate the contents of a simple .app.src file %% app(Name) -> "{application, " ++ atom_to_list(Name) ++ ", [{vsn, \"3\"}, {foo, ok}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}.\n". rebar-2.6.0/inttest/appup_src/000077500000000000000000000000001254103774400163075ustar00rootroot00000000000000rebar-2.6.0/inttest/appup_src/appup_src_rt.erl000066400000000000000000000047711254103774400215250ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Luis Rascao (luis.rascao@gmail.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(appup_src_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {copy, "src", "src"}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), LineRegexp = "Compiled src/app\.appup\.src", ?assertEqual(true, has_line(Output, LineRegexp)), %% check that ebin/app.appup exists ?assertMatch(true, filelib:is_regular("ebin/app.appup")), retest_log:log(debug, "Generated ebin/app.appup~n", []), %% check that ebin/app.appup has expected version {ok, [{AppVersion, [{UpgradeFrom, _} | _UpgradeFromRest], [{DowngradeTo, _} | _DowngradeToRest]}]} = file:consult("ebin/app.appup"), ?assertEqual(AppVersion, "%VSN%"), ?assertEqual(UpgradeFrom, "1.3"), ?assertEqual(DowngradeTo, "1.3"), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. rebar-2.6.0/inttest/appup_src/src/000077500000000000000000000000001254103774400170765ustar00rootroot00000000000000rebar-2.6.0/inttest/appup_src/src/app.app.src000066400000000000000000000001651254103774400211500ustar00rootroot00000000000000{application, app, [{vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}. rebar-2.6.0/inttest/appup_src/src/app.appup.src000066400000000000000000000142241254103774400215160ustar00rootroot00000000000000{"%VSN%", [ {"1.3", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {add_module, asn1rt_per_bin}, {add_module, asn1rt_check}, {add_module, asn1rt_per_bin_rt2ct}, {add_module, asn1rt_ber_bin_v2}, {add_module, asn1rt_driver_handler}, {remove, {asn1rt_ber_v1, soft_purge, soft_purge}} ] }, {"1.3.1", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {add_module, asn1rt_per_bin}, {add_module, asn1rt_check}, {add_module, asn1rt_per_bin_rt2ct}, {add_module, asn1rt_ber_bin_v2}, {add_module, asn1rt_driver_handler}, {remove, {asn1rt_ber_v1, soft_purge, soft_purge}} ] }, {"1.3.1.1", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {add_module, asn1rt_per_bin}, {add_module, asn1rt_check}, {add_module, asn1rt_per_bin_rt2ct}, {add_module, asn1rt_ber_bin_v2}, {add_module, asn1rt_driver_handler}, {remove, {asn1rt_ber_v1, soft_purge, soft_purge}} ] }, {"1.3.2", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin, soft_purge, soft_purge, []}, {load_module, asn1rt_check, soft_purge, soft_purge, []}, {add_module, asn1rt_per_bin_rt2ct}, {add_module, asn1rt_ber_bin_v2}, {add_module, asn1rt_driver_handler}, {remove, {asn1rt_ber_v1, soft_purge, soft_purge}} ] }, {"1.3.3", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin, soft_purge, soft_purge, []}, {load_module, asn1rt_check, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_bin_v2}, {add_module, asn1rt_driver_handler}, {remove, {asn1rt_ber_v1, soft_purge, soft_purge}} ] }, {"1.3.3.1", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin, soft_purge, soft_purge, []}, {load_module, asn1rt_check, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_bin_v2}, {add_module, asn1rt_driver_handler}, {remove, {asn1rt_ber_v1, soft_purge, soft_purge}} ] } ], [ {"1.3", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_v1}, {remove, {asn1rt_per_bin, soft_purge, soft_purge}}, {remove, {asn1rt_check, soft_purge, soft_purge}}, {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}}, {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}}, {remove, {asn1rt_driver_handler, soft_purge, soft_purge}} ] }, {"1.3.1", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_v1}, {remove, {asn1rt_per_bin, soft_purge, soft_purge}}, {remove, {asn1rt_check, soft_purge, soft_purge}}, {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}}, {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}}, {remove, {asn1rt_driver_handler, soft_purge, soft_purge}} ] }, {"1.3.1.1", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_v1}, {remove, {asn1rt_per_bin, soft_purge, soft_purge}}, {remove, {asn1rt_check, soft_purge, soft_purge}}, {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}}, {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}}, {remove, {asn1rt_driver_handler, soft_purge, soft_purge}} ] }, {"1.3.2", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin, soft_purge, soft_purge, []}, {load_module, asn1rt_check, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_v1}, {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}}, {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}}, {remove, {asn1rt_driver_handler, soft_purge, soft_purge}} ] }, {"1.3.3", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin, soft_purge, soft_purge, []}, {load_module, asn1rt_check, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_v1}, {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}}, {remove, {asn1rt_driver_handler, soft_purge, soft_purge}} ] }, {"1.3.3.1", [ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []}, {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []}, {load_module, asn1rt, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin, soft_purge, soft_purge, []}, {load_module, asn1rt_check, soft_purge, soft_purge, []}, {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []}, {add_module, asn1rt_ber_v1}, {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}}, {remove, {asn1rt_driver_handler, soft_purge, soft_purge}} ] } ]}. rebar-2.6.0/inttest/appup_src/src/appup_src.erl000066400000000000000000000000761254103774400216010ustar00rootroot00000000000000-module(appup_src). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/appup_src_2/000077500000000000000000000000001254103774400165305ustar00rootroot00000000000000rebar-2.6.0/inttest/appup_src_2/appup_src_2_rt.erl000066400000000000000000000045471254103774400221700ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Luis Rascao (luis.rascao@gmail.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(appup_src_2_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {copy, "src", "src"}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), LineRegexp = "Compiled src/app\.appup\.src", ?assertEqual(true, has_line(Output, LineRegexp)), %% check that ebin/app.appup exists ?assertMatch(true, filelib:is_regular("ebin/app.appup")), retest_log:log(debug, "Generated ebin/app.appup~n", []), %% check that ebin/app.appup has expected version {ok, [{AppVersion, [], []}]} = file:consult("ebin/app.appup"), ?assertEqual(AppVersion, "1.24"), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. rebar-2.6.0/inttest/appup_src_2/appup_src_rt_2.erl000066400000000000000000000047301254103774400221620ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Luis Rascao (luis.rascao@gmail.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(appup_src_rt_2). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {copy, "src", "src"}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), LineRegexp = "Compiled src/app\.appup\.src", ?assertEqual(true, has_line(Output, LineRegexp)), %% check that ebin/app.appup exists ?assertMatch(true, filelib:is_regular("ebin/app.appup")), retest_log:log(debug, "Generated ebin/app.appup~n", []), %% check that ebin/app.appup has expected version {ok, [{AppVersion, [{UpgradeFrom, _}], [{DowngradeTo, _}]}]} = file:consult("ebin/app.appup"), ?assertEqual(AppVersion, "1.2.2"), ?assertEqual(UpgradeFrom, "1.2.1"), ?assertEqual(DowngradeTo, "1.2.1"), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. rebar-2.6.0/inttest/appup_src_2/src/000077500000000000000000000000001254103774400173175ustar00rootroot00000000000000rebar-2.6.0/inttest/appup_src_2/src/app.app.src000066400000000000000000000001651254103774400213710ustar00rootroot00000000000000{application, app, [{vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}. rebar-2.6.0/inttest/appup_src_2/src/app.appup.src000066400000000000000000000000251254103774400217310ustar00rootroot00000000000000{"1.24", [], [] }. rebar-2.6.0/inttest/appup_src_2/src/appup_src.erl000066400000000000000000000000761254103774400220220ustar00rootroot00000000000000-module(appup_src). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/appup_src_script/000077500000000000000000000000001254103774400176735ustar00rootroot00000000000000rebar-2.6.0/inttest/appup_src_script/appup_src_script_rt.erl000066400000000000000000000047351254103774400244750ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Luis Rascao (luis.rascao@gmail.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(appup_src_script_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {copy, "src", "src"}]. run(Dir) -> retest_log:log(debug, "Running in Dir: ~s~n", [Dir]), {ok, [_Pid|Output]} = retest:sh("./rebar compile -vv", [{async, false}]), LineRegexp = "Compiled src/app\.appup\.src", ?assertEqual(true, has_line(Output, LineRegexp)), %% check that ebin/app.appup exists ?assertMatch(true, filelib:is_regular("ebin/app.appup")), retest_log:log(debug, "Generated ebin/app.appup~n", []), %% check that ebin/app.appup has expected version {ok, [{AppVersion, [{UpgradeFrom, _}], [{DowngradeTo, _}]}]} = file:consult("ebin/app.appup"), ?assertEqual(AppVersion, "2.1.2"), ?assertEqual(UpgradeFrom, "2.1.1"), ?assertEqual(DowngradeTo, "2.1.1"), ok. has_line([], _RE) -> false; has_line([L|T], RE) -> case re:run(L, RE, []) of {match, _Captured} -> true; match -> true; nomatch -> has_line(T, RE) end. rebar-2.6.0/inttest/appup_src_script/src/000077500000000000000000000000001254103774400204625ustar00rootroot00000000000000rebar-2.6.0/inttest/appup_src_script/src/app.app.src000066400000000000000000000001651254103774400225340ustar00rootroot00000000000000{application, app, [{vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}. rebar-2.6.0/inttest/appup_src_script/src/app.appup.src000066400000000000000000000002041254103774400230730ustar00rootroot00000000000000{"1.2.2", [{"1.2.1", [{update,proc1,{advanced,[]},[]}, {update,proc2,{advanced,[]},[]}] }], [{"1.2.1", []}]}. rebar-2.6.0/inttest/appup_src_script/src/app.appup.src.script000066400000000000000000000003711254103774400244030ustar00rootroot00000000000000%% decompose the appup [{AppVersion, [{UpgradeFrom, UpgradeInstructions}], [{DowngradeTo, DowngradeInstructions}]}] = CONFIG, %% and replace the version strings [{"2.1.2", [{"2.1.1", UpgradeInstructions}], [{"2.1.1", DowngradeInstructions}]}]. rebar-2.6.0/inttest/appup_src_script/src/appup_src.erl000066400000000000000000000000761254103774400231650ustar00rootroot00000000000000-module(appup_src). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/bug_5_rt.erl000066400000000000000000000013431254103774400165260ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -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.6.0/inttest/code_path_no_recurse/000077500000000000000000000000001254103774400204655ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl000066400000000000000000000034771254103774400260630ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tuncer Ayaz %% %% 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(code_path_no_recurse_rt). -export([files/0, run/1]). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "src", "src"}, {copy, "test", "test"}, {copy, "deps", "deps"} ]. run(_Dir) -> retest:log(info, "Compile project~n"), {ok, _} = retest:sh("./rebar -v compile"), retest:log(info, "Run eunit with referenced deps on the code path~n"), {ok, _} = retest:sh("./rebar -v eunit"), ok. rebar-2.6.0/inttest/code_path_no_recurse/deps/000077500000000000000000000000001254103774400214205ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/bazdep/000077500000000000000000000000001254103774400226655ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/bazdep/src/000077500000000000000000000000001254103774400234545ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src000066400000000000000000000003231254103774400262070ustar00rootroot00000000000000{application, bazdep, [ {description, ""}, {vsn, "1"}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, {bazdep, []}}, {env, []} ]}. rebar-2.6.0/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl000066400000000000000000000001001254103774400254140ustar00rootroot00000000000000-module(bazdep). -export([bazdep/0]). bazdep() -> bazdep. rebar-2.6.0/inttest/code_path_no_recurse/deps/bazdep/test/000077500000000000000000000000001254103774400236445ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl000066400000000000000000000001731254103774400270400ustar00rootroot00000000000000-module(bazdep_tests). -include_lib("eunit/include/eunit.hrl"). bazdep_test() -> ?assert(bazdep:bazdep() =:= bazdep). rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/000077500000000000000000000000001254103774400226745ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/rebar.config000066400000000000000000000000311254103774400251500ustar00rootroot00000000000000{deps, [{bazdep, "1"}]}. rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/src/000077500000000000000000000000001254103774400234635ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src000066400000000000000000000003231254103774400262250ustar00rootroot00000000000000{application, foodep, [ {description, ""}, {vsn, "1"}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, {foodep, []}}, {env, []} ]}. rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl000066400000000000000000000001241254103774400254400ustar00rootroot00000000000000-module(foodep). -export([foodep/0]). foodep() -> bazdep:bazdep() =:= bazdep. rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/test/000077500000000000000000000000001254103774400236535ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl000066400000000000000000000001601254103774400270520ustar00rootroot00000000000000-module(foodep_tests). -include_lib("eunit/include/eunit.hrl"). foodep_test() -> ?assert(foodep:foodep()). rebar-2.6.0/inttest/code_path_no_recurse/deps/unuseddep/000077500000000000000000000000001254103774400234145ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/unuseddep/src/000077500000000000000000000000001254103774400242035ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src000066400000000000000000000003311254103774400274640ustar00rootroot00000000000000{application, unuseddep, [ {description, ""}, {vsn, "1"}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, {unuseddep, []}}, {env, []} ]}. rebar-2.6.0/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl000066400000000000000000000001141254103774400266770ustar00rootroot00000000000000-module(unuseddep). -export([unuseddep/0]). unuseddep() -> unuseddep. rebar-2.6.0/inttest/code_path_no_recurse/rebar.config000066400000000000000000000000311254103774400227410ustar00rootroot00000000000000{deps, [{foodep, "1"}]}. rebar-2.6.0/inttest/code_path_no_recurse/src/000077500000000000000000000000001254103774400212545ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/src/codepath.app.src000066400000000000000000000003271254103774400243350ustar00rootroot00000000000000{application, codepath, [ {description, ""}, {vsn, "1"}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, {codepath, []}}, {env, []} ]}. rebar-2.6.0/inttest/code_path_no_recurse/src/codepath.erl000066400000000000000000000001171254103774400235460ustar00rootroot00000000000000-module(codepath). -export([codepath/0]). codepath() -> foodep:foodep(). rebar-2.6.0/inttest/code_path_no_recurse/test/000077500000000000000000000000001254103774400214445ustar00rootroot00000000000000rebar-2.6.0/inttest/code_path_no_recurse/test/codepath_tests.erl000066400000000000000000000007121254103774400251610ustar00rootroot00000000000000-module(codepath_tests). -include_lib("eunit/include/eunit.hrl"). codepath_test() -> ?assertEqual({module, codepath}, code:ensure_loaded(codepath)), ?assertEqual({module, foodep}, code:ensure_loaded(foodep)), ?assertEqual({module, bazdep}, code:ensure_loaded(bazdep)), ?assert(codepath:codepath()). unuseddep_test() -> ?assertEqual(non_existing, code:which(unuseddep)), ?assertEqual({error, nofile}, code:ensure_loaded(unuseddep)). rebar-2.6.0/inttest/cover/000077500000000000000000000000001254103774400154315ustar00rootroot00000000000000rebar-2.6.0/inttest/cover/cover_rt.erl000066400000000000000000000054161254103774400177660ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Brian H. Ward %% %% 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(cover_rt). -export([files/0,run/1]). -include_lib("eunit/include/eunit.hrl"). files() -> [{create, "ebin/foo.app", app(foo)}, {copy, "../../rebar","rebar"}, {copy, "src", "src"}, {copy, "rebar-cover_export_json.config", "rebar-cover_export_json.config"}]. run(_Dir) -> ifdef_test(), cover_export_json_test(), ok. ifdef_test() -> {ok, Output} = retest:sh("./rebar -v eunit"), io:format("output => ~p~n", [Output]), ?assert(check_output(Output, "foo")), {ok, Listing} = file:list_dir(".eunit"), ?assert(check_output(Listing, "foo.beam")), ?assertMatch({ok,_}, retest:sh("./rebar clean")). cover_export_json_test() -> {ok, Output} = retest:sh("./rebar -v -C rebar-cover_export_json.config eunit"), ?assert(check_output(Output, "foo")), ?assertEqual( {ok, <<"{\"module\":\"foo\",\"covered\":2,\"not_covered\":1}">>}, file:read_file(".eunit/foo.COVER.json")), ?assertMatch( {ok, _}, retest:sh("./rebar -C rebar-cover_export_json.config clean")). check_output(Output,Target) -> lists:any(fun(Line) -> string:str(Line, Target) > 0 end, Output). 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.6.0/inttest/cover/rebar-cover_export_json.config000066400000000000000000000000621254103774400234570ustar00rootroot00000000000000{cover_enabled, true}. {cover_export_json, true}. rebar-2.6.0/inttest/cover/src/000077500000000000000000000000001254103774400162205ustar00rootroot00000000000000rebar-2.6.0/inttest/cover/src/foo.erl000066400000000000000000000004221254103774400175050ustar00rootroot00000000000000-module(foo). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. covered_function() -> "I am tested". uncovered_function() -> "I am not tested". -ifdef(EUNIT). covered_function_test() -> ?assertEqual("I am tested", covered_function()). -endif. rebar-2.6.0/inttest/ct1/000077500000000000000000000000001254103774400150025ustar00rootroot00000000000000rebar-2.6.0/inttest/ct1/app.config000066400000000000000000000001341254103774400167470ustar00rootroot00000000000000%% This file is an application config file, not a CT test config file [{a1, [{foo, bar}]}]. rebar-2.6.0/inttest/ct1/ct1_rt.erl000066400000000000000000000014101254103774400166760ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(ct1_rt). -compile(export_all). files() -> [{create, "ebin/a1.app", app(a1)}, {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "app.config", "app.config"}, {copy, "test_SUITE.erl", "itest/test_SUITE.erl"}]. run(_Dir) -> {ok, _} = retest:sh("./rebar compile ct"), {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.6.0/inttest/ct1/rebar.config000066400000000000000000000001111254103774400172550ustar00rootroot00000000000000{ct_dir, "itest"}. {ct_extra_params, "-repeat 2 -erl_args -config app"}. rebar-2.6.0/inttest/ct1/test_SUITE.erl000066400000000000000000000005011254103774400174320ustar00rootroot00000000000000-module(test_SUITE). -compile(export_all). -include_lib("ct.hrl"). all() -> [simple_test, app_config_file_test]. simple_test(Config) -> io:format("Test: ~p\n", [Config]). app_config_file_test(_Config) -> application:start(a1), {ok, bar} = application:get_env(a1, foo), application:stop(a1). rebar-2.6.0/inttest/ct2/000077500000000000000000000000001254103774400150035ustar00rootroot00000000000000rebar-2.6.0/inttest/ct2/ct2_rt.erl000066400000000000000000000020641254103774400167060ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(ct2_rt). -compile(export_all). files() -> [{create, "ebin/foo.app", app(foo)}, {copy, "../../rebar", "rebar"}, {copy, "foo.test.spec", "foo.test.spec"}, {copy, "deps/bar.test.spec", "deps/bar.test.spec"}, {copy, "foo_SUITE.erl", "test/foo_SUITE.erl"}]. run(_Dir) -> Ref = retest:sh("./rebar compile ct -vvv", [async]), {ok, [[CTRunCmd]]} = retest:sh_expect(Ref, "^\"ct_run.*", [global, {capture, first, binary}]), {match, _} = re:run(CTRunCmd, "foo.test.spec", [global]), %% deps/bar.test.spec should be ignored by rebar_ct:collect_glob/3 nomatch = re:run(CTRunCmd, "bar.test.spec", [global]), 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.6.0/inttest/ct2/deps/000077500000000000000000000000001254103774400157365ustar00rootroot00000000000000rebar-2.6.0/inttest/ct2/deps/bar.test.spec000066400000000000000000000000441254103774400203320ustar00rootroot00000000000000%% this test spec should be ignored rebar-2.6.0/inttest/ct2/foo.test.spec000066400000000000000000000000271254103774400174170ustar00rootroot00000000000000{suites, "test", all}. rebar-2.6.0/inttest/ct2/foo_SUITE.erl000066400000000000000000000002401254103774400172370ustar00rootroot00000000000000-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.6.0/inttest/ct3/000077500000000000000000000000001254103774400150045ustar00rootroot00000000000000rebar-2.6.0/inttest/ct3/app.config000066400000000000000000000001341254103774400167510ustar00rootroot00000000000000%% This file is an application config file, not a CT test config file [{a1, [{foo, bar}]}]. rebar-2.6.0/inttest/ct3/converted/000077500000000000000000000000001254103774400167755ustar00rootroot00000000000000rebar-2.6.0/inttest/ct3/converted/.keep000066400000000000000000000000001254103774400177100ustar00rootroot00000000000000rebar-2.6.0/inttest/ct3/ct3_rt.erl000066400000000000000000000041341254103774400167100ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Savchuk Igor %% %% 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(ct3_rt). -compile(export_all). files() -> [{create, "ebin/a1.app", app(a1)}, {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "app.config", "itest/app.config"}, {copy, "test_SUITE.erl", "itest/test_SUITE.erl"}, {copy, "converted"}, {copy, "unconverted"}]. run(_Dir) -> {ok, _} = retest:sh("./rebar compile ct -v", [{env, [{"ERL_FLAGS", "-name ct_rt3"}]}]), 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.6.0/inttest/ct3/rebar.config000066400000000000000000000001331254103774400172630ustar00rootroot00000000000000{ct_dir, "itest"}. {ct_extra_params, "-repeat 2 -pa converted -erl_args -pa unconverted"}. rebar-2.6.0/inttest/ct3/test_SUITE.erl000066400000000000000000000037271254103774400174510ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Savchuk Igor %% %% 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(test_SUITE). -compile(export_all). -include_lib("ct.hrl"). all() -> [simple_test, app_config_file_test, check_path]. simple_test(Config) -> io:format("Test: ~p\n", [Config]). app_config_file_test(_Config) -> application:start(a1), {ok, bar} = application:get_env(a1, foo), application:stop(a1). %% check if -erl_args in ct_extra_params of rebar.config is working %% if it is, path before -erl_args is converted to absolute %% and path after -erl_args is not check_path(_Config) -> Path = code:get_path(), false = lists:member("converted", Path), true = lists:member("unconverted", Path). rebar-2.6.0/inttest/ct3/unconverted/000077500000000000000000000000001254103774400173405ustar00rootroot00000000000000rebar-2.6.0/inttest/ct3/unconverted/.keep000066400000000000000000000000001254103774400202530ustar00rootroot00000000000000rebar-2.6.0/inttest/depplugins/000077500000000000000000000000001254103774400164655ustar00rootroot00000000000000rebar-2.6.0/inttest/depplugins/base_dir_cwd_plugin.erl000066400000000000000000000004051254103774400231530ustar00rootroot00000000000000-module(base_dir_cwd_plugin). -export([pre_compile/2]). pre_compile(_, _) -> File = "base_dir_cwd_pre.compile", ok = file:write_file(File, <<"base_dir cwd pre_compile plugin">>), rebar_log:log(info, "Wrote ~p/~s~n", [rebar_utils:get_cwd(), File]). rebar-2.6.0/inttest/depplugins/dep_cwd_plugin.erl000066400000000000000000000003661254103774400221610ustar00rootroot00000000000000-module(dep_cwd_plugin). -export([pre_compile/2]). pre_compile(_, _) -> File = "dep_cwd_pre.compile", ok = file:write_file(File, <<"dep cwd pre_compile plugin">>), rebar_log:log(info, "Wrote ~p/~s~n", [rebar_utils:get_cwd(), File]). rebar-2.6.0/inttest/depplugins/depplugins_rt.erl000066400000000000000000000050101254103774400220440ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %%% @doc Plugin handling test %%% %%% This test checks if plugins are loaded correctly. %%% %%% It has three applications: %%%
    %%%
  1. fish. top-level app, has one dependency: `dependsonplugin'. %%% It also loads a plugin from CWD which creates %%% base_dir_cwd_pre.compile on pre_compile.
  2. %%%
  3. dependsonplugin, has one dependency: `testplugin' and loads %%% the testplugin_mod plugin.
  4. %%%
  5. testplugin. This is a plugin application which creates %%% plugin_pre.compile on pre_compile. It also loads a plugin from CWD %%% which creates dep_cwd_pre.compile on pre_compile.
  6. %%%
-module(depplugins_rt). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "base_dir_cwd_plugin.erl", "base_dir_cwd_plugin.erl"}, {create, "ebin/fish.app", app(fish, [])}, {copy, "rebar_dependsonplugin.config", "deps/dependsonplugin/rebar.config"}, {create, "deps/dependsonplugin/ebin/dependsonplugin.app", app(dependsonplugin, [])}, {copy, "rebar_testplugin.config", "deps/testplugin/rebar.config"}, {copy, "testplugin_mod.erl", "deps/testplugin/plugins/testplugin_mod.erl"}, {copy, "dep_cwd_plugin.erl", "deps/testplugin/dep_cwd_plugin.erl"}, {create, "deps/testplugin/ebin/testplugin.app", app(testplugin, [])} ]. run(_Dir) -> ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), ?assertEqual(true, filelib:is_regular("base_dir_cwd_pre.compile")), ?assertEqual(true, filelib:is_regular( "deps/dependsonplugin/base_dir_cwd_pre.compile")), ?assertEqual(true, filelib:is_regular( "deps/dependsonplugin/plugin_pre.compile")), ?assertEqual(true, filelib:is_regular( "deps/testplugin/base_dir_cwd_pre.compile")), ?assertEqual(true, filelib:is_regular( "deps/testplugin/dep_cwd_pre.compile")), ?assertEqual(true, filelib:is_regular( "deps/testplugin/plugin_pre.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.6.0/inttest/depplugins/rebar.config000066400000000000000000000000751254103774400207510ustar00rootroot00000000000000{deps, [dependsonplugin]}. {plugins, [base_dir_cwd_plugin]}. rebar-2.6.0/inttest/depplugins/rebar_dependsonplugin.config000066400000000000000000000000631254103774400242240ustar00rootroot00000000000000{deps, [testplugin]}. {plugins, [testplugin_mod]}. rebar-2.6.0/inttest/depplugins/rebar_testplugin.config000066400000000000000000000000351254103774400232230ustar00rootroot00000000000000{plugins, [dep_cwd_plugin]}. rebar-2.6.0/inttest/depplugins/testplugin_mod.erl000066400000000000000000000003411254103774400222240ustar00rootroot00000000000000-module(testplugin_mod). -export([pre_compile/2]). pre_compile(_, _) -> File = "plugin_pre.compile", ok = file:write_file(File, <<"Yadda!">>), rebar_log:log(info, "Wrote ~p/~s~n", [rebar_utils:get_cwd(), File]). rebar-2.6.0/inttest/erlc/000077500000000000000000000000001254103774400152405ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/asn1/000077500000000000000000000000001254103774400161025ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/asn1/SIMPLE-ASN.asn1000066400000000000000000000001411254103774400202720ustar00rootroot00000000000000SIMPLE-ASN DEFINITIONS ::= BEGIN SimpleMessage ::= SEQUENCE { id INTEGER } END rebar-2.6.0/inttest/erlc/erlc_rt.erl000066400000000000000000000140571254103774400174050ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tuncer Ayaz %% %% 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(erlc_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). -define(MODULES, [after_first_erl, first_xrl, first_yrl, first_erl, foo, foo_app, foo_sup, foo_test_worker, foo_worker, 'SIMPLE-ASN']). -define(BEAM_FILES, ["after_first_erl.beam", "first_xrl.beam", "first_yrl.beam", "first_erl.beam", "foo.beam", "foo_app.beam", "foo_sup.beam", "foo_test_worker.beam", "foo_worker.beam", "SIMPLE-ASN.beam"]). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "rebar-no_debug_info.config", "rebar-no_debug_info.config"}, {copy, "include", "include"}, {copy, "extra-include", "extra-include"}, {copy, "src", "src"}, {copy, "extra-src", "extra-src"}, {copy, "mibs", "mibs"}, {copy, "asn1", "asn1"}, {create, "ebin/foo.app", app(foo, ?MODULES)}, %% deps {create, "deps/foobar/ebin/foobar.app", app(foobar, [foobar])}, {copy, "foobar.erl", "deps/foobar/src/foobar.erl"} ]. run(_Dir) -> ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), ok = check_beams(true), ok = check_debug_info(true), MibResult = filename:join(["priv", "mibs", "SIMPLE-MIB.bin"]), ?assertMatch(true, filelib:is_regular(MibResult)), ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])), ok = check_beams(false), ?assertMatch(false, filelib:is_regular(MibResult)), ?assertMatch( {ok, _}, retest_sh:run("./rebar -C rebar-no_debug_info.config compile", [])), ok = check_beams(true), ok = check_debug_info(false), ?assertMatch(true, filelib:is_regular(MibResult)), %% Regression test for https://github.com/rebar/rebar/issues/249 %% %% Root cause: We didn't have per-project .rebar/erlcinfo but just one in %% /.rebar/erlcinfo. %% %% Solution: Ensure every project has its own .rebar/erlcinfo %% %% For the bug to happen, the following conditions must be met: %% %% 1. /rebar.config has erl_first_files %% 2. one of the 'first' files depends on another file (in this %% case via -include_lib()) %% 3. a sub project's rebar.config, if any, has no erl_first_files entry %% %% Now because erl_first_files is retrieved via rebar_config:get_list/3, %% base_dir/rebar.config's erl_first_files is inherited, and because we had %% a shared /.rebar/erlcinfo instead of one per project, the %% cached entry was reused. Next, while compiling the sub project %% rebar_erlc_compiler:needs_compile/3 gets a last modification time of %% zero for the 'first' file which does not exist inside the sub project. %% This, and the fact that it has at least one dependency, makes %% needs_compile/3 return 'true'. The root cause is that we didn't have per %% project .rebar/erlcinfo. For /.rebar/erlcinfo to be populated, %% base_dir has to be compiled at least once. Therefore, after the first %% compile any compile processing the sub project will fail because %% needs_compile/3 will always return true for the non-existent 'first' %% file. ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), ok = check_beams(true), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), ok = check_beams(true), ok. check_beams(Exist) -> check_files(Exist, fun filelib:is_regular/1). check_debug_info(HasDebugInfo) -> check_files(HasDebugInfo, fun has_debug_info/1). check_files(Expected, Check) -> lists:foreach( fun(F) -> File = filename:join("ebin", F), ?assertEqual(Expected, Check(File)) end, ?BEAM_FILES). %% NOTE: Copied from dialyzer_utils:get_abstract_code_from_beam/1 and %% modified for local use. We could have called the function directly, %% but dialyzer_utils is not an official API to rely on. has_debug_info(File) -> case beam_lib:chunks(File, [abstract_code]) of {ok, {_Mod, List}} -> case lists:keyfind(abstract_code, 1, List) of {abstract_code, {raw_abstract_v1, _Abstr}} -> true; _ -> false end; _ -> false end. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/erlc/extra-include/000077500000000000000000000000001254103774400200045ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/extra-include/foo_extra.hrl000066400000000000000000000002021254103774400224730ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -define(FOO_EXTRA, foo_extra). rebar-2.6.0/inttest/erlc/extra-src/000077500000000000000000000000001254103774400171505ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/extra-src/after_first_erl.erl000066400000000000000000000003761254103774400230340ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(after_first_erl). -compile({parse_transform, first_erl}). -include_lib("eunit/include/eunit.hrl"). -export([test/0]). test() -> ?debugHere. rebar-2.6.0/inttest/erlc/extra-src/foo_sup.erl000066400000000000000000000006111254103774400213240ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(foo_sup). -behavior(supervisor). -export([start_link/0, init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init(_Args) -> FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]}, {ok,{{one_for_all,1,1}, [FooChild]}}. rebar-2.6.0/inttest/erlc/foobar.erl000066400000000000000000000002351254103774400172140ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(foobar). -export([test/0]). test() -> true. rebar-2.6.0/inttest/erlc/include/000077500000000000000000000000001254103774400166635ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/include/foo_core.hrl000066400000000000000000000002001254103774400211550ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -define(FOO_CORE, foo_core). rebar-2.6.0/inttest/erlc/mibs/000077500000000000000000000000001254103774400161725ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/mibs/SIMPLE-MIB.mib000066400000000000000000000010501254103774400202550ustar00rootroot00000000000000-- SIMPLE-MIB. -- This is just a simple MIB used for testing! -- SIMPLE-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, enterprises FROM SNMPv2-SMI; ericsson MODULE-IDENTITY LAST-UPDATED "201403060000Z" ORGANIZATION "rebar" CONTACT-INFO "rebar or whoever is currently responsible for the SIMPLE enterprise MIB tree branch (enterprises.999)." DESCRIPTION "This very small module is made available for mib-compilation testing." ::= { enterprises 999 } END rebar-2.6.0/inttest/erlc/rebar-no_debug_info.config000066400000000000000000000004731254103774400223210ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_first_files, ["src/first_xrl.erl", "src/first_yrl.erl"]}. {erl_opts, [ no_debug_info, {i, "extra-include"}, {src_dirs, ["src", "extra-src"]}, {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} ]}. rebar-2.6.0/inttest/erlc/rebar.config000066400000000000000000000005641254103774400175270ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_first_files, ["src/first_xrl.erl", "src/first_yrl.erl", "src/first_erl.erl", "extra-src/after_first_erl.erl"]}. {deps, [foobar]}. {erl_opts, [ {i, "extra-include"}, {src_dirs, ["extra-src", "src"]}, {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} ]}. rebar-2.6.0/inttest/erlc/src/000077500000000000000000000000001254103774400160275ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/src/._do_not_compile.erl000066400000000000000000000002161254103774400217410ustar00rootroot00000000000000syntax error this file is here to verify that rebar does not try to compile files like OS X resource forks and should not be processed at all rebar-2.6.0/inttest/erlc/src/behaviour/000077500000000000000000000000001254103774400200135ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc/src/behaviour/foo_worker.erl000066400000000000000000000003311254103774400226700ustar00rootroot00000000000000-module(foo_worker). -ifdef(NO_CALLBACK_ATTRIBUTE). -export([behaviour_info/1]). behaviour_info(callbacks) -> [{status, 0}]; behaviour_info(_) -> undefined. -else. -callback status() -> 'idle' | 'busy'. -endif. rebar-2.6.0/inttest/erlc/src/first_erl.erl000066400000000000000000000004231254103774400205230ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(first_erl). -include_lib("eunit/include/eunit.hrl"). -export([test/0, parse_transform/2]). test() -> ?debugHere. parse_transform(Forms, _Options) -> Forms. rebar-2.6.0/inttest/erlc/src/first_xrl.xrl000066400000000000000000000003121254103774400205660ustar00rootroot00000000000000Definitions. D = [0-9] Rules. {D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}. {D}+\.{D}+((E|e)(\+|\-)?{D}+)? : {token,{float,TokenLine,list_to_float(TokenChars)}}. Erlang code. rebar-2.6.0/inttest/erlc/src/first_yrl.yrl000066400000000000000000000003151254103774400205730ustar00rootroot00000000000000Nonterminals list elements element. Terminals atom '(' ')'. Rootsymbol list. list -> '(' ')'. list -> '(' elements ')'. elements -> element. elements -> element elements. element -> atom. element -> list. rebar-2.6.0/inttest/erlc/src/foo.erl000066400000000000000000000013661254103774400173240ustar00rootroot00000000000000-module(foo). -export([start_link/0, start_link/1, init/1, terminate/2, handle_info/2, handle_call/3, handle_cast/2, code_change/3]). -behavior(gen_server). -include("foo_core.hrl"). -include("foo_extra.hrl"). -include_lib("kernel/include/file.hrl"). -record(state, {node :: node()}). start_link() -> start_link(undefined). start_link(Args) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []). init(_Args) -> {ok, #state{node=node()}}. terminate(_Reason, _Data) -> ok. handle_info(_Info, State) -> {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_call(_Msg, _From, State) -> {reply, ok, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. rebar-2.6.0/inttest/erlc/src/foo_app.erl000066400000000000000000000002251254103774400201550ustar00rootroot00000000000000-module(foo_app). -behaviour(application). -export([start/2, stop/1]). start(_Type, _Args) -> foo_sup:start_link(). stop(_State) -> ok. rebar-2.6.0/inttest/erlc/src/foo_test_worker.erl000066400000000000000000000013131254103774400217440ustar00rootroot00000000000000-module(foo_test_worker). -behaviour(gen_server). -behaviour(foo_worker). -export([start_link/0, start_link/1, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, status/0]). -include_lib("kernel/include/inet.hrl"). start_link() -> start_link(undefined). start_link(Args) -> gen_server:start_link(?MODULE, Args, []). init([]) -> {ok, undefined}. handle_call(_Event, _From, State) -> {reply, ok, State}. handle_cast(_Event, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. status() -> busy. rebar-2.6.0/inttest/erlc_dep_graph/000077500000000000000000000000001254103774400172515ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc_dep_graph/erlc_dep_graph_rt.erl000066400000000000000000000077131254103774400234300ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 David Kubecka %% %% 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(erlc_dep_graph_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). files() -> [{copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "src", "src"}, {copy, "include", "include"}, {copy, "extra_include", "extra_include"}]. run(_Dir) -> compile_all(ok, ""), check_beams_ok(), check_beams_untouched(filelib:wildcard("ebin/*.beam")), modify_and_recompile_ok("src/lisp.erl", "ebin/lisp.beam"), clean_all_ok(), compile_all(error, "-C rebar.config.non-existing"), compile_all(ok, ""), modify_and_recompile_ok("extra_include/extra.hrl", "ebin/java.beam"), Java = "src/java.erl", {ok, OrigContent} = file:read_file(Java), %% Remove header file inclusion {ok, _} = file:copy("src/java.erl.no_extra", Java), %% Ensure recompilation touch([Java]), compile_all(ok, ""), %% Modify that header file touch(["extra_include/extra.hrl"]), %% Ensure we don't have to recompile anything check_beams_untouched(["ebin/java.beam"]), %% Clean up ok = file:write_file(Java, OrigContent), %% Check that changes propagate deeply through the dependency tree modify_and_recompile_ok("include/lambda.hrl", "ebin/perl.beam"), ok. check_beams_ok() -> F = fun(BeamFile) -> ?assert(filelib:is_regular(BeamFile)) end, with_erl_beams(F). check_beams_untouched(Beams) -> compile_all_and_assert_mtimes(Beams, fun erlang:'=:='/2). modify_and_recompile_ok(TouchFile, CheckFile) -> touch([TouchFile]), compile_all_and_assert_mtimes([CheckFile], fun erlang:'<'/2). compile_all_and_assert_mtimes(Beams, Cmp) -> BeamsModifiedBefore = mtime_ns(Beams), compile_all(ok, ""), BeamsModifiedAfter = mtime_ns(Beams), lists:zipwith(fun(Before, After) -> ?assert(Cmp(Before, After)) end, BeamsModifiedBefore, BeamsModifiedAfter). with_erl_beams(F) -> lists:map( fun(ErlFile) -> ErlRoot = filename:rootname(filename:basename(ErlFile)), BeamFile = filename:join("ebin", ErlRoot ++ ".beam"), F(BeamFile) end, filelib:wildcard("src/*.erl")). mtime_ns(Files) -> [os:cmd("stat -c%y " ++ File) || File <- Files]. touch(Files) -> %% Sleep one second so that filelib:last_modified/1 is guaranteed to notice %% that files have changed. ok = timer:sleep(1000), [os:cmd("touch " ++ File) || File <- Files]. compile_all(Result, Opts) -> ?assertMatch({Result, _}, retest_sh:run("./rebar " ++ Opts ++ " compile", [])). clean_all_ok() -> ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])). rebar-2.6.0/inttest/erlc_dep_graph/extra_include/000077500000000000000000000000001254103774400220775ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc_dep_graph/extra_include/extra.hrl000066400000000000000000000002011254103774400237220ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -define(CONCISE, impossible). rebar-2.6.0/inttest/erlc_dep_graph/include/000077500000000000000000000000001254103774400206745ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc_dep_graph/include/lambda.hrl000066400000000000000000000001671254103774400226270ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -define(FUN, fake). rebar-2.6.0/inttest/erlc_dep_graph/rebar.config000066400000000000000000000003051254103774400215310ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_opts, [ {i, "extra_include"}, {parse_transform, lisp}, {parse_transform, pascal} ]}. rebar-2.6.0/inttest/erlc_dep_graph/src/000077500000000000000000000000001254103774400200405ustar00rootroot00000000000000rebar-2.6.0/inttest/erlc_dep_graph/src/foo.app.src000066400000000000000000000003041254103774400221100ustar00rootroot00000000000000{application,foo, [{description,[]}, {vsn,"1.0.0"}, {registered,[]}, {applications,[kernel,stdlib]}, {env,[]} ]}.rebar-2.6.0/inttest/erlc_dep_graph/src/java.erl000066400000000000000000000003161254103774400214650ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(java). -export([factory/0]). -include("lambda.hrl"). -include("extra.hrl"). factory() -> ?FUN. rebar-2.6.0/inttest/erlc_dep_graph/src/java.erl.no_extra000066400000000000000000000002671254103774400233100ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(java). -export([factory/0]). -include("lambda.hrl"). factory() -> ?FUN. rebar-2.6.0/inttest/erlc_dep_graph/src/lisp.erl000066400000000000000000000004411254103774400215120ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(lisp). -export([parse_transform/2]). -include("lambda.hrl"). -ifdef(NOT_DEFINED). -include_lib("include/non/existent.hrl"). -endif. parse_transform(Forms, _Options) -> Forms. rebar-2.6.0/inttest/erlc_dep_graph/src/pascal.erl000066400000000000000000000003031254103774400220030ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(pascal). -export([parse_transform/2]). parse_transform(Forms, _Options) -> Forms. rebar-2.6.0/inttest/erlc_dep_graph/src/perl.erl000066400000000000000000000003001254103774400214770ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et -module(perl). -export(['$_'/0]). -compile({parse_transform, lisp}). '$_'() -> anything. rebar-2.6.0/inttest/eunit/000077500000000000000000000000001254103774400154375ustar00rootroot00000000000000rebar-2.6.0/inttest/eunit/eunit_rt.erl000066400000000000000000000025421254103774400177770ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(eunit_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). files() -> [{create, "ebin/foo.app", app(foo)}, {copy, "../../rebar", "rebar"}, {copy, "src", "src"}, {copy, "eunit_src", "eunit_src"}, {copy, "rebar-eunit_compile_opts.config", "rebar-eunit_compile_opts.config"}]. run(_Dir) -> ifdef_test(), eunit_compile_opts_test(), ok. ifdef_test() -> {ok, Output} = retest:sh("./rebar -v eunit"), ?assert(check_output(Output, "foo_test")), ?assertMatch({ok, _}, retest:sh("./rebar clean")). eunit_compile_opts_test() -> {ok, Output} = retest:sh("./rebar -v -C rebar-eunit_compile_opts.config eunit"), ?assert(check_output(Output, "bar_test")), ?assertMatch( {ok, _}, retest:sh("./rebar -C rebar-eunit_compile_opts.config clean")). check_output(Output, Target) -> lists:any(fun(Line) -> string:str(Line, Target) > 0 end, Output). %% %% Generate the contents of a simple .app file %% app(Name) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/eunit/eunit_src/000077500000000000000000000000001254103774400174325ustar00rootroot00000000000000rebar-2.6.0/inttest/eunit/eunit_src/bar.erl000066400000000000000000000001321254103774400206760ustar00rootroot00000000000000-module(bar). -include_lib("eunit/include/eunit.hrl"). bar_test() -> ?assert(true). rebar-2.6.0/inttest/eunit/rebar-eunit_compile_opts.config000066400000000000000000000000631254103774400236170ustar00rootroot00000000000000{eunit_compile_opts, [{src_dirs, ["eunit_src"]}]}. rebar-2.6.0/inttest/eunit/src/000077500000000000000000000000001254103774400162265ustar00rootroot00000000000000rebar-2.6.0/inttest/eunit/src/foo.erl000066400000000000000000000001621254103774400175140ustar00rootroot00000000000000-module(foo). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). foo_test() -> ?assert(true). -endif. rebar-2.6.0/inttest/logging/000077500000000000000000000000001254103774400157415ustar00rootroot00000000000000rebar-2.6.0/inttest/logging/logging_rt.erl000066400000000000000000000114351254103774400206040ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tuncer Ayaz %% %% 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(logging_rt). -export([files/0, run/1]). -define(APP_FILE, "ebin/logging.app"). files() -> [ {copy, "../../rebar", "rebar"}, {create, ?APP_FILE, app(invalid_name, [])} ]. run(_Dir) -> SharedExpected = "==> logging_rt \\(compile\\)", %% provoke ERROR due to an invalid app file retest:log(info, "Check 'compile' failure output~n"), ok = check_output("./rebar compile -q", should_fail, [SharedExpected, "ERROR: "], ["WARN: ", "INFO: ", "DEBUG: "]), %% fix bad app file ok = file:write_file(?APP_FILE, app(logging, [])), retest:log(info, "Check 'compile' success output~n"), ok = check_output("./rebar compile", should_succeed, [SharedExpected], ["ERROR: ", "WARN: ", "INFO: ", "DEBUG: "]), retest:log(info, "Check 'compile -v' success output~n"), ok = check_output("./rebar compile -v", should_succeed, [SharedExpected], ["ERROR: ", "INFO: ", "DEBUG: "]), retest:log(info, "Check 'compile -vv' success output~n"), ok = check_output("./rebar compile -vv", should_succeed, [SharedExpected, "DEBUG: "], ["ERROR: ", "INFO: "]), ok. check_output(Cmd, FailureMode, Expected, Unexpected) -> case {retest:sh(Cmd), FailureMode} of {{error, _}=Error, should_succeed} -> retest:log(error, "cmd '~s' failed:~n~p~n", [Cmd, Error]), Error; {{ok, Captured}, should_succeed} -> Joined = string:join(Captured, "\n"), check_output1(Cmd, Joined, Expected, Unexpected); {{error, {stopped, {_Rc, Captured}}}, should_fail} -> Joined = string:join(Captured, "\n"), check_output1(Cmd, Joined, Expected, Unexpected) end. check_output1(Cmd, Captured, Expected, Unexpected) -> ReOpts = [{capture, all, list}], ExMatches = lists:zf( fun(Pattern) -> case re:run(Captured, Pattern, ReOpts) of nomatch -> retest:log(error, "Expected pattern '~s' missing " "in the following output:~n" "=== BEGIN ===~n~s~n=== END ===~n", [Pattern, Captured]), {true, Pattern}; {match, _} -> false end end, Expected), UnExMatches = lists:zf( fun(Pattern) -> case re:run(Captured, Pattern, ReOpts) of nomatch -> false; {match, [Match]} -> retest:log( error, "Unexpected output when running cmd '~s':~n~s~n", [Cmd, Match]), {true, Match} end end, Unexpected), case {ExMatches, UnExMatches} of {[], []} -> ok; _ -> error end. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/port/000077500000000000000000000000001254103774400152775ustar00rootroot00000000000000rebar-2.6.0/inttest/port/c_src/000077500000000000000000000000001254103774400163705ustar00rootroot00000000000000rebar-2.6.0/inttest/port/c_src/test1.c000066400000000000000000000000231254103774400175670ustar00rootroot00000000000000#include "test1.h" rebar-2.6.0/inttest/port/c_src/test1.h000066400000000000000000000000001254103774400175670ustar00rootroot00000000000000rebar-2.6.0/inttest/port/c_src/test2.c000066400000000000000000000000231254103774400175700ustar00rootroot00000000000000#include "test2.h" rebar-2.6.0/inttest/port/c_src/test2.h000066400000000000000000000000231254103774400175750ustar00rootroot00000000000000#include "test1.h" rebar-2.6.0/inttest/port/port_rt.erl000066400000000000000000000075671254103774400175130ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tomas Janousek %% %% 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(port_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "c_src", "c_src"}, {create, "ebin/foo.app", app(foo, [])} ]. run(_Dir) -> %% wait a bit for new files to have different timestamps wait(), %% test.so is created during first compile ?assertEqual(0, filelib:last_modified("priv/test.so")), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), TestSo1 = filelib:last_modified("priv/test.so"), ?assert(TestSo1 > 0), wait(), %% nothing happens during second compile ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), TestSo2 = filelib:last_modified("priv/test.so"), Test1o2 = filelib:last_modified("c_src/test1.o"), Test2o2 = filelib:last_modified("c_src/test2.o"), ?assertEqual(TestSo1, TestSo2), ?assert(TestSo1 >= Test1o2), ?assert(TestSo1 >= Test2o2), wait(), %% when test2.c changes, at least test2.o and test.so are rebuilt ?assertMatch({ok, _}, retest_sh:run("touch c_src/test2.c", [])), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), TestSo3 = filelib:last_modified("priv/test.so"), Test2o3 = filelib:last_modified("c_src/test2.o"), ?assert(TestSo3 > TestSo2), ?assert(Test2o3 > TestSo2), wait(), %% when test2.h changes, at least test2.o and test.so are rebuilt ?assertMatch({ok, _}, retest_sh:run("touch c_src/test2.h", [])), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), TestSo4 = filelib:last_modified("priv/test.so"), Test2o4 = filelib:last_modified("c_src/test2.o"), ?assert(TestSo4 > TestSo3), ?assert(Test2o4 > TestSo3), wait(), %% when test1.h changes, everything is rebuilt ?assertMatch({ok, _}, retest_sh:run("touch c_src/test1.h", [])), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), TestSo5 = filelib:last_modified("priv/test.so"), Test1o5 = filelib:last_modified("c_src/test1.o"), Test2o5 = filelib:last_modified("c_src/test2.o"), ?assert(TestSo5 > TestSo4), ?assert(Test1o5 > TestSo4), ?assert(Test2o5 > TestSo4), ok. wait() -> timer:sleep(1000). %% %% 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.6.0/inttest/port/rebar.config000066400000000000000000000000611254103774400175560ustar00rootroot00000000000000{port_specs, [{"priv/test.so", ["c_src/*.c"]}]}. rebar-2.6.0/inttest/profile/000077500000000000000000000000001254103774400157535ustar00rootroot00000000000000rebar-2.6.0/inttest/profile/profile_rt.erl000066400000000000000000000157731254103774400206410ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tuncer Ayaz %% %% 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(profile_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). files() -> [ {copy, "../../rebar", "rebar"} ]. run(_Dir) -> Cmd = "./rebar list-deps", FprofFiles = fprof_files(), EflameFiles = ["eflame.trace", "eflame.svg"], %% run a simple command (list-deps) without profiling SharedExpected = "==> profile_rt \\(list-deps\\)", %% run Cmd without profiling retest:log(info, "Check '~s' without profiling~n", [Cmd]), ok = check(Cmd, should_succeed, [SharedExpected], ["Profiling!"], [], FprofFiles ++ EflameFiles), %% run Cmd with fprof profiling retest:log(info, "Check '~s' with fprof profiling~n", [Cmd]), ok = check(Cmd ++ " -p", should_succeed, [SharedExpected, "Profiling!", "See fprof\.analysis"], ["See eflame\.svg"], FprofFiles, EflameFiles), delete_files(FprofFiles), %% run Cmd with explicitly selected fprof profiling retest:log(info, "Check '~s' with explicitly selected fprof profiling~n", [Cmd]), ok = check(Cmd ++ " -p profiler=fprof", should_succeed, [SharedExpected, "Profiling!", "See fprof\.analysis"], ["See eflame\.svg"], FprofFiles, EflameFiles), delete_files(FprofFiles), case code:lib_dir(eflame) of {error, bad_name} -> retest:log(info, "eflame not found in code path. skip eflame test~n"), ok; _EflameDir -> %% run Cmd with eflame profiling retest:log(info, "Check '~s' with eflame profiling~n", [Cmd]), ok = check(Cmd ++ " -p profiler=eflame", should_succeed, [SharedExpected, "Profiling!", "See eflame\.svg"], ["See fprof\.analysis"], EflameFiles, FprofFiles), delete_files(EflameFiles) end, ok. fprof_files() -> FprofFiles = ["fprof.trace", "fprof.analysis"], CgrindFiles = ["fprof.cgrind"], case os:find_executable("erlgrind") of false -> retest:log(info, "erlgrind escript not found. skip fprof.cgrind check~n"), FprofFiles; _ErlGrind -> FprofFiles ++ CgrindFiles end. check(Cmd, FailureMode, ExpectedOutput, UnexpectedOutput, ExpectedFiles, UnexpectedFiles) -> case {retest:sh(Cmd), FailureMode} of {{error, _}=Error, should_succeed} -> retest:log(error, "cmd '~s' failed:~n~p~n", [Cmd, Error]), Error; {{ok, CapturedOutput}, should_succeed} -> JoinedOutput = string:join(CapturedOutput, "\n"), check1(Cmd, JoinedOutput, ExpectedOutput, UnexpectedOutput, ExpectedFiles, UnexpectedFiles); {{error, {stopped, {_Rc, CapturedOutput}}}, should_fail} -> JoinedOutput = string:join(CapturedOutput, "\n"), check1(Cmd, JoinedOutput, ExpectedOutput, UnexpectedOutput, ExpectedFiles, UnexpectedFiles) end. check1(Cmd, CapturedOutput, ExpectedOutput, UnexpectedOutput, ExpectedFiles, UnexpectedFiles) -> ReOpts = [{capture, all, list}], ExMatches = lists:zf( fun(Pattern) -> case re:run(CapturedOutput, Pattern, ReOpts) of nomatch -> retest:log(error, "Expected pattern '~s' missing " "in the following output:~n" "=== BEGIN ===~n~s~n=== END ===~n", [Pattern, CapturedOutput]), {true, Pattern}; {match, _} -> false end end, ExpectedOutput), UnExMatches = lists:zf( fun(Pattern) -> case re:run(CapturedOutput, Pattern, ReOpts) of nomatch -> false; {match, [Match]} -> retest:log( console, "Unexpected output when running cmd '~s':~n~s~n", [Cmd, Match]), {true, Match} end end, UnexpectedOutput), ExFiles = lists:zf( fun(File) -> case filelib:is_regular(File) of true -> false; false -> retest:log(error, "Expected file missing: ~s~n", [File]), {true, File} end end, ExpectedFiles), UnExFiles = lists:zf( fun(File) -> case filelib:is_regular(File) of true -> retest:log(error, "Unexpected file found: ~s~n", [File]), {true, File}; false -> false end end, UnexpectedFiles), case {ExMatches, UnExMatches, ExFiles, UnExFiles} of {[], [], [], []} -> ok; _ -> error end. delete_files(Files) -> lists:foreach(fun(File) -> ok = file:delete(File) end, Files). %% %% 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.6.0/inttest/proto_gpb/000077500000000000000000000000001254103774400163065ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/include/000077500000000000000000000000001254103774400177315ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/include/.gitignore000066400000000000000000000001071254103774400217170ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignore rebar-2.6.0/inttest/proto_gpb/mock/000077500000000000000000000000001254103774400172375ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/mock/gpb/000077500000000000000000000000001254103774400200075ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/mock/gpb/src/000077500000000000000000000000001254103774400205765ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/mock/gpb/src/gpb.app.src000066400000000000000000000004011254103774400226310ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et {application, gpb, [{description, "Simple mock of gpb, with enough to generate dummy files"}, {vsn, "1"}, {registered, []}, {applications, [kernel, stdlib]}, {env, []}]}. rebar-2.6.0/inttest/proto_gpb/mock/gpb/src/gpb.erl000066400000000000000000000001331254103774400220470ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(gpb). rebar-2.6.0/inttest/proto_gpb/mock/gpb/src/gpb_compile.erl000066400000000000000000000060641254103774400235700ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Tomas Abrahamsson (tomas.abrahamsson@gmail.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(gpb_compile). -export([file/2]). %% Simulate gpb compiling some proto files, %% but generate only enough of what's needed for testing -- dummy stuff only. %% if a bad.proto file is supplied then gpb fails file(Proto, Opts) -> ok = case filename:basename(Proto) of "bad.proto" -> error; _ -> ok end, Prefix = proplists:get_value(module_name_prefix, Opts, ""), Suffix = proplists:get_value(module_name_suffix, Opts, ""), ProtoBase = filename:basename(Proto, ".proto"), ModBase = Prefix ++ ProtoBase ++ Suffix, ErlDest = filename:join(get_erl_outdir(Opts), ModBase ++ ".erl"), HrlDest = filename:join(get_hrl_outdir(Opts), ModBase ++ ".hrl"), ok = file:write_file(ErlDest, erl_text(ModBase)), ok = file:write_file(HrlDest, hrl_text(ModBase)). erl_text(ModBase) -> io_lib:format( lines(["-module(~p).", "-export([encode_msg/1]).", "-export([decode_msg/2]).", "", "encode_msg(some_dummy_msg) -> <<1,2,3>>.", "", "decode_msg(<<1,2,3>>, _) -> some_dummy_msg."]), [list_to_atom(ModBase)]). hrl_text(ModBase) -> io_lib:format( lines(["-ifndef(~s_hrl).", "-define(~s_hrl, true).", "", "%% some record definitions would normally go here...", "" "-endif. %% ~s_hrl"]), [ModBase, ModBase, ModBase]). get_erl_outdir(Opts) -> proplists:get_value(o_erl, Opts, get_outdir(Opts)). get_hrl_outdir(Opts) -> proplists:get_value(o_hrl, Opts, get_outdir(Opts)). get_outdir(Opts) -> proplists:get_value(o, Opts, "."). lines(Lines) -> lists:flatten([[L, $\n] || L <- Lines]). rebar-2.6.0/inttest/proto_gpb/proto.bad/000077500000000000000000000000001254103774400201765ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto.bad/a/000077500000000000000000000000001254103774400204165ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto.bad/a/b/000077500000000000000000000000001254103774400206375ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto.bad/a/b/test3.proto000066400000000000000000000004121254103774400227630ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test3; service test3 { rpc testRpc3(RPC_INPUT3) returns (RPC_OUTPUT3); } message RPC_INPUT3 { optional string str = 1; } message RPC_OUTPUT3 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto.bad/a/test2.proto000066400000000000000000000004121254103774400225410ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test2; service test2 { rpc testRpc2(RPC_INPUT2) returns (RPC_OUTPUT2); } message RPC_INPUT2 { optional string str = 1; } message RPC_OUTPUT2 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto.bad/bad.proto000066400000000000000000000004521254103774400220120ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test; service test { rpc testRpc(RPC_INPUT) returns (RPC_OUTPUT); } message RPC_INPUT // bug introduced intentionally here optional string str = 1; } message RPC_OUTPUT { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto.bad/c/000077500000000000000000000000001254103774400204205ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto.bad/c/d/000077500000000000000000000000001254103774400206435ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto.bad/c/d/test5.proto000066400000000000000000000004121254103774400227710ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test5; service test5 { rpc testRpc5(RPC_INPUT5) returns (RPC_OUTPUT5); } message RPC_INPUT5 { optional string str = 1; } message RPC_OUTPUT5 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto.bad/c/test4.proto000066400000000000000000000004121254103774400225450ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test4; service test4 { rpc testRpc4(RPC_INPUT4) returns (RPC_OUTPUT4); } message RPC_INPUT4 { optional string str = 1; } message RPC_OUTPUT4 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto.bad/test.proto000066400000000000000000000004031254103774400222370ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test; service test { rpc testRpc(RPC_INPUT) returns (RPC_OUTPUT); } message RPC_INPUT { optional string str = 1; } message RPC_OUTPUT { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto/000077500000000000000000000000001254103774400174515ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto/a/000077500000000000000000000000001254103774400176715ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto/a/b/000077500000000000000000000000001254103774400201125ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto/a/b/test3.proto000066400000000000000000000004121254103774400222360ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test3; service test3 { rpc testRpc3(RPC_INPUT3) returns (RPC_OUTPUT3); } message RPC_INPUT3 { optional string str = 1; } message RPC_OUTPUT3 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto/a/test2.proto000066400000000000000000000004121254103774400220140ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test2; service test2 { rpc testRpc2(RPC_INPUT2) returns (RPC_OUTPUT2); } message RPC_INPUT2 { optional string str = 1; } message RPC_OUTPUT2 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto/c/000077500000000000000000000000001254103774400176735ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto/c/d/000077500000000000000000000000001254103774400201165ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/proto/c/d/test5.proto000066400000000000000000000004121254103774400222440ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test5; service test5 { rpc testRpc5(RPC_INPUT5) returns (RPC_OUTPUT5); } message RPC_INPUT5 { optional string str = 1; } message RPC_OUTPUT5 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto/c/test4.proto000066400000000000000000000004121254103774400220200ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test4; service test4 { rpc testRpc4(RPC_INPUT4) returns (RPC_OUTPUT4); } message RPC_INPUT4 { optional string str = 1; } message RPC_OUTPUT4 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto/test.proto000066400000000000000000000004031254103774400215120ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test; service test { rpc testRpc(RPC_INPUT) returns (RPC_OUTPUT); } message RPC_INPUT { optional string str = 1; } message RPC_OUTPUT { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/proto_gpb_rt.erl000066400000000000000000000166671254103774400215320ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Luis Rascão (luis.rascao@gmail.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(proto_gpb_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). -include_lib("kernel/include/file.hrl"). -include_lib("deps/retest/include/retest.hrl"). -define(MODULES, [foo, foo_app, foo_sup]). -define(GENERATED_MODULES, [test_gpb, test2_gpb, test3_gpb, test4_gpb, test5_gpb]). -define(SOURCE_PROTO_FILES, ["test.proto", "a/test2.proto", "a/b/test3.proto", "c/test4.proto", "c/d/test5.proto"]). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "rebar2.config", "rebar2.config"}, {copy, "rebar.bad.config", "rebar.bad.config"}, {copy, "include", "include"}, {copy, "src", "src"}, {copy, "proto", "proto"}, {copy, "proto.bad", "proto.bad"}, {copy, "mock", "deps"}, {create, "ebin/foo.app", app(foo, ?MODULES ++ ?GENERATED_MODULES)} ]. run(_Dir) -> % perform test obtaining the .proto files from src dir ok = run_from_dir(success_expected, "src", "rebar.config"), % perform test obtaining the .proto files from proto dir ok = run_from_dir(success_expected, "proto", "rebar2.config"), % perform a test where a failure is expected ok = run_from_dir(fail_expected, "proto.bad", "rebar.bad.config"). run_from_dir(fail_expected, _ProtoDir, ConfigFile) -> %% we expect a failure to happen, however rebar should not crash; %% We make sure of that by scanning the error. {error, {stopped, {1, Error}}} = retest_sh:run("./rebar --config " ++ ConfigFile ++ " compile", []), %% No matches of the string 'EXIT' should occur, these %% indicate a rebar crash and not a exit with error. 0 = string:str(lists:flatten(Error), "'EXIT'"), ok; run_from_dir(success_expected, ProtoDir, ConfigFile) -> ?assertMatch({ok, _}, retest_sh:run("./rebar --config " ++ ConfigFile ++ " clean", [])), ?assertMatch({ok, _}, retest_sh:run("./rebar --config " ++ ConfigFile ++ " compile", [])), %% Foo includes test_gpb.hrl, %% So if it compiled, that also means gpb succeeded in %% generating the test_gpb.hrl file, and also that it generated %% the .hrl file was generated before foo was compiled. ok = check_beams_generated(), ?DEBUG("Verifying recompilation~n", []), TestErl = hd(generated_erl_files()), TestProto = hd(source_proto_files(ProtoDir)), make_proto_newer_than_erl(TestProto, TestErl), TestMTime1 = read_mtime(TestErl), ?assertMatch({ok, _}, retest_sh:run("./rebar --config " ++ ConfigFile ++ " compile", [])), TestMTime2 = read_mtime(TestErl), ?assert(TestMTime2 > TestMTime1), ?DEBUG("Verifying recompilation with no changes~n", []), TestMTime3 = read_mtime(TestErl), ?assertMatch({ok, _}, retest_sh:run("./rebar --config " ++ ConfigFile ++ " compile", [])), TestMTime4 = read_mtime(TestErl), ?assert(TestMTime3 =:= TestMTime4), ?DEBUG("Verify cleanup~n", []), ?assertMatch({ok, _}, retest_sh:run("./rebar --config " ++ ConfigFile ++ " clean", [])), ok = check_files_deleted(), ok. check_beams_generated() -> check(fun filelib:is_regular/1, beam_files()). check_files_deleted() -> check(fun file_does_not_exist/1, beam_files() ++ generated_erl_files() ++ generated_hrl_files()). beam_files() -> add_dir("ebin", add_ext(?MODULES, ".beam")). generated_erl_files() -> add_dir("src", add_ext(?GENERATED_MODULES, ".erl")). generated_hrl_files() -> add_dir("include", add_ext(?GENERATED_MODULES, ".hrl")). generated_beam_files() -> add_dir("ebin", add_ext(?GENERATED_MODULES, ".beam")). source_proto_files(ProtoDir) -> add_dir(ProtoDir, ?SOURCE_PROTO_FILES). file_does_not_exist(F) -> not filelib:is_regular(F). add_ext(Modules, Ext) -> [lists:concat([Module, Ext]) || Module <- Modules]. add_dir(Dir, Files) -> [filename:join(Dir, File) || File <- Files]. read_mtime(File) -> {ok, #file_info{mtime=MTime}} = file:read_file_info(File), MTime. make_proto_newer_than_erl(Proto, Erl) -> %% Do this by back-dating the erl file instead of touching the %% proto file. Do this instead of sleeping for a second to get a %% reliable test. Sleeping would have been needed sin ce the %% #file_info{} (used by eg. filelib:last_modified) does not have %% sub-second resolution (even though most file systems have). {ok, #file_info{mtime=ProtoMTime}} = file:read_file_info(Proto), {ok, ErlInfo} = file:read_file_info(Erl), OlderMTime = update_seconds_to_datetime(ProtoMTime, -2), OlderErlInfo = ErlInfo#file_info{mtime = OlderMTime}, ok = file:write_file_info(Erl, OlderErlInfo). update_seconds_to_datetime(DT, ToAdd) -> calendar:gregorian_seconds_to_datetime( calendar:datetime_to_gregorian_seconds(DT) + ToAdd). touch_file(File) -> ?assertMatch({ok, _}, retest_sh:run("touch " ++ File, [])). check(Check, Files) -> lists:foreach( fun(F) -> ?assertMatch({true, _}, {Check(F), F}) end, Files). %% %% 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, gpb]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/proto_gpb/rebar.bad.config000066400000000000000000000010371254103774400213160ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_opts, [ {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} ]}. {deps, [ %% The dependency below to gpb is needed for "rebar compile" to %% work, thus for the inttest to work, but the gpb that is actually %% used in inttest is brought in from the inttest/proto_gpb/mock %% subdirectory. {gpb, ".*"} ]}. {proto_opts, [ {compiler, gpb}, {src_dirs, ["proto.bad"]} ]}. {gpb_opts, [{module_name_suffix, "_gpb"}]}. rebar-2.6.0/inttest/proto_gpb/rebar.config000066400000000000000000000010001254103774400205570ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_opts, [ {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} ]}. {deps, [ %% The dependency below to gpb is needed for "rebar compile" to %% work, thus for the inttest to work, but the gpb that is actually %% used in inttest is brought in from the inttest/proto_gpb/mock %% subdirectory. {gpb, ".*"} ]}. {proto_opts, [ {compiler, gpb} ]}. {gpb_opts, [{module_name_suffix, "_gpb"}]}. rebar-2.6.0/inttest/proto_gpb/rebar2.config000066400000000000000000000010331254103774400206470ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_opts, [ {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} ]}. {deps, [ %% The dependency below to gpb is needed for "rebar compile" to %% work, thus for the inttest to work, but the gpb that is actually %% used in inttest is brought in from the inttest/proto_gpb/mock %% subdirectory. {gpb, ".*"} ]}. {proto_opts, [ {compiler, gpb}, {src_dirs, ["proto"]} ]}. {gpb_opts, [{module_name_suffix, "_gpb"}]}. rebar-2.6.0/inttest/proto_gpb/src/000077500000000000000000000000001254103774400170755ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/src/a/000077500000000000000000000000001254103774400173155ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/src/a/b/000077500000000000000000000000001254103774400175365ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/src/a/b/test3.proto000066400000000000000000000004121254103774400216620ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test3; service test3 { rpc testRpc3(RPC_INPUT3) returns (RPC_OUTPUT3); } message RPC_INPUT3 { optional string str = 1; } message RPC_OUTPUT3 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/src/a/test2.proto000066400000000000000000000004121254103774400214400ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test2; service test2 { rpc testRpc2(RPC_INPUT2) returns (RPC_OUTPUT2); } message RPC_INPUT2 { optional string str = 1; } message RPC_OUTPUT2 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/src/c/000077500000000000000000000000001254103774400173175ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/src/c/d/000077500000000000000000000000001254103774400175425ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_gpb/src/c/d/test5.proto000066400000000000000000000004121254103774400216700ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test5; service test5 { rpc testRpc5(RPC_INPUT5) returns (RPC_OUTPUT5); } message RPC_INPUT5 { optional string str = 1; } message RPC_OUTPUT5 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/src/c/test4.proto000066400000000000000000000004121254103774400214440ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test4; service test4 { rpc testRpc4(RPC_INPUT4) returns (RPC_OUTPUT4); } message RPC_INPUT4 { optional string str = 1; } message RPC_OUTPUT4 { optional string str = 1; } rebar-2.6.0/inttest/proto_gpb/src/foo.erl000066400000000000000000000016411254103774400203660ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(foo). -export([start_link/0, start_link/1, init/1, terminate/2, handle_info/2, handle_call/3, handle_cast/2, code_change/3]). -behavior(gen_server). -include("../include/test_gpb.hrl"). -include("../include/test2_gpb.hrl"). -include("../include/test3_gpb.hrl"). -include("../include/test4_gpb.hrl"). -include("../include/test5_gpb.hrl"). -record(state, {node :: node()}). start_link() -> start_link(undefined). start_link(Args) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []). init(_Args) -> {ok, #state{node=node()}}. terminate(_Reason, _Data) -> ok. handle_info(_Info, State) -> {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_call(_Msg, _From, State) -> {reply, ok, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. rebar-2.6.0/inttest/proto_gpb/src/foo_app.erl000066400000000000000000000003411254103774400212220ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(foo_app). -behaviour(application). -export([start/2, stop/1]). start(_Type, _Args) -> foo_sup:start_link(). stop(_State) -> ok. rebar-2.6.0/inttest/proto_gpb/src/foo_sup.erl000066400000000000000000000005621254103774400212560ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(foo_sup). -behavior(supervisor). -export([start_link/0, init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init(_Args) -> FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]}, {ok,{{one_for_all,1,1}, [FooChild]}}. rebar-2.6.0/inttest/proto_gpb/src/test.proto000066400000000000000000000004031254103774400211360ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et package test; service test { rpc testRpc(RPC_INPUT) returns (RPC_OUTPUT); } message RPC_INPUT { optional string str = 1; } message RPC_OUTPUT { optional string str = 1; } rebar-2.6.0/inttest/proto_protobuffs/000077500000000000000000000000001254103774400177275ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_protobuffs/include/000077500000000000000000000000001254103774400213525ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_protobuffs/include/.gitignore000066400000000000000000000001071254103774400233400ustar00rootroot00000000000000# Ignore everything in this directory * # Except this file !.gitignore rebar-2.6.0/inttest/proto_protobuffs/mock/000077500000000000000000000000001254103774400206605ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_protobuffs/mock/protobuffs/000077500000000000000000000000001254103774400230515ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_protobuffs/mock/protobuffs/src/000077500000000000000000000000001254103774400236405ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_protobuffs/mock/protobuffs/src/protobuffs.app.src000066400000000000000000000004311254103774400273170ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et {application, protobuffs, [{description, "Simple mock of erlang_protobuffs, with enough to generate dummy files"}, {vsn, "1"}, {registered, []}, {applications, [kernel, stdlib]}, {env, []}]}. rebar-2.6.0/inttest/proto_protobuffs/mock/protobuffs/src/protobuffs.erl000066400000000000000000000001421254103774400265320ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(protobuffs). rebar-2.6.0/inttest/proto_protobuffs/mock/protobuffs/src/protobuffs_compile.erl000066400000000000000000000052101254103774400302430ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Tomas Abrahamsson (tomas.abrahamsson@gmail.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(protobuffs_compile). -export([scan_file/2]). %% Simulate protobuffs compiling some proto files, %% but generate only enough of what's needed for testing -- dummy stuff only. scan_file(Proto, _Opts) -> ProtoBase = filename:basename(Proto, ".proto"), ModBase = ProtoBase ++ "_pb", BeamDest = filename:join(get_beam_outdir(), ModBase ++ ".beam"), HrlDest = filename:join(get_hrl_outdir(), ModBase ++ ".hrl"), ok = file:write_file(BeamDest, beam_text(ModBase)), ok = file:write_file(HrlDest, hrl_text(ModBase)). beam_text(ModBase) -> Mod = list_to_atom(ModBase), Forms = [mk_attr(module, Mod)], % just a -module(...). line {ok, Mod, Bin} = compile:forms(Forms), Bin. mk_attr(AttrName, AttrValue) -> erl_syntax:revert( erl_syntax:attribute(erl_syntax:atom(AttrName), [erl_syntax:abstract(AttrValue)])). hrl_text(ModBase) -> io_lib:format( lines(["-ifndef(~s_hrl).", "-define(~s_hrl, true).", "", "%% some record definitions would normally go here...", "" "-endif. %% ~s_hrl"]), [ModBase, ModBase, ModBase]). get_beam_outdir() -> ".". get_hrl_outdir() -> ".". lines(Lines) -> lists:flatten([[L, $\n] || L <- Lines]). rebar-2.6.0/inttest/proto_protobuffs/proto_protobuffs_rt.erl000066400000000000000000000051451254103774400245610ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tomas Abrahamsson (tomas.abrahamsson@gmail.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(proto_protobuffs_rt). -export([files/0, run/1]). -include_lib("eunit/include/eunit.hrl"). -define(MODULES, [foo, foo_app, foo_sup, test_pb]). -define(BEAM_FILES, ["foo.beam", "foo_app.beam", "foo_sup.beam", "test_pb.beam"]). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {copy, "include", "include"}, {copy, "src", "src"}, {copy, "mock", "deps"}, {create, "ebin/foo.app", app(foo, ?MODULES)} ]. run(_Dir) -> ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])), ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), ok = check_beams_generated(), ok. check_beams_generated() -> lists:foreach( fun(F) -> File = filename:join("ebin", F), ?assert(filelib:is_regular(File)) end, ?BEAM_FILES). %% %% 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, gpb]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/proto_protobuffs/rebar.config000066400000000000000000000010661254103774400222140ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_opts, [ {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'} ]}. {deps, [ %% The dependency below to protobuffs is needed for "rebar compile" to %% work, thus for the inttest to work, but the protobuffs that is actually %% used in inttest is brought in from the inttest/proto_protobuffs/mock %% subdirectory. {protobuffs, ".*"} ]}. %% The default proto compiler is protobuffs %% so don't need to specify this %% {proto_compiler, protobuffs}. rebar-2.6.0/inttest/proto_protobuffs/src/000077500000000000000000000000001254103774400205165ustar00rootroot00000000000000rebar-2.6.0/inttest/proto_protobuffs/src/foo.erl000066400000000000000000000014101254103774400220010ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(foo). -export([start_link/0, start_link/1, init/1, terminate/2, handle_info/2, handle_call/3, handle_cast/2, code_change/3]). -behavior(gen_server). -include("../include/test_pb.hrl"). -record(state, {node :: node()}). start_link() -> start_link(undefined). start_link(Args) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []). init(_Args) -> {ok, #state{node=node()}}. terminate(_Reason, _Data) -> ok. handle_info(_Info, State) -> {noreply, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_call(_Msg, _From, State) -> {reply, ok, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. rebar-2.6.0/inttest/proto_protobuffs/src/foo_app.erl000066400000000000000000000003411254103774400226430ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(foo_app). -behaviour(application). -export([start/2, stop/1]). start(_Type, _Args) -> foo_sup:start_link(). stop(_State) -> ok. rebar-2.6.0/inttest/proto_protobuffs/src/foo_sup.erl000066400000000000000000000005621254103774400226770ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(foo_sup). -behavior(supervisor). -export([start_link/0, init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init(_Args) -> FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]}, {ok,{{one_for_all,1,1}, [FooChild]}}. rebar-2.6.0/inttest/proto_protobuffs/src/test.proto000066400000000000000000000002421254103774400225600ustar00rootroot00000000000000// -*- c-basic-offset: 4; indent-tabs-mode: nil -*- // ex: ts=4 sw=4 et message m1 { optional string str = 1; } message m2 { optional string str = 1; } rebar-2.6.0/inttest/require_vsn/000077500000000000000000000000001254103774400166555ustar00rootroot00000000000000rebar-2.6.0/inttest/require_vsn/rebar.config000066400000000000000000000002101254103774400211300ustar00rootroot00000000000000{require_erts_vsn, "no_such_erts_vsn-1.2"}. {require_otp_vsn, "no_such_otp_vsn-1.2"}. {require_min_otp_vsn, "no_such_min_otp_vsn-1.0"}. rebar-2.6.0/inttest/require_vsn/require_vsn_rt.erl000066400000000000000000000105101254103774400224250ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tuncer Ayaz %% %% 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(require_vsn_rt). -export([files/0, run/1]). files() -> [ {copy, "../../rebar", "rebar"}, {copy, "rebar.config", "rebar.config"}, {create, "ebin/require_vsn.app", app(require_vsn, [])} ]. run(_Dir) -> SharedExpected = "==> require_vsn_rt \\(compile\\)", %% Provoke ABORT due to failed require vsn check. retest:log(info, "Check require vsn failure~n"), ok = check_output("./rebar compile", should_fail, [SharedExpected, "ERROR: "], ["WARN: "]), %% Treat version constraints as warnings. retest:log(info, "Check require vsn success with -k/--keep-going~n"), ok = check_output("./rebar -k compile", should_succeed, [SharedExpected, "ERROR: "], ["WARN: "]), ok. check_output(Cmd, FailureMode, Expected, Unexpected) -> case {retest:sh(Cmd), FailureMode} of {{error, _}=Error, should_succeed} -> retest:log(error, "cmd '~s' failed:~n~p~n", [Cmd, Error]), Error; {{ok, Captured}, should_succeed} -> Joined = string:join(Captured, "\n"), check_output1(Cmd, Joined, Expected, Unexpected); {{error, {stopped, {_Rc, Captured}}}, should_fail} -> Joined = string:join(Captured, "\n"), check_output1(Cmd, Joined, Expected, Unexpected) end. check_output1(Cmd, Captured, Expected, Unexpected) -> ReOpts = [{capture, all, list}], ExMatches = lists:zf( fun(Pattern) -> case re:run(Captured, Pattern, ReOpts) of nomatch -> retest:log(error, "Expected pattern '~s' missing " "in the following output:~n" "=== BEGIN ===~n~s~n=== END ===~n", [Pattern, Captured]), {true, Pattern}; {match, _} -> false end end, Expected), UnExMatches = lists:zf( fun(Pattern) -> case re:run(Captured, Pattern, ReOpts) of nomatch -> false; {match, [Match]} -> retest:log( console, "Unexpected output when running cmd '~s':~n~s~n", [Cmd, Match]), {true, Match} end end, Unexpected), case {ExMatches, UnExMatches} of {[], []} -> ok; _ -> error end. %% %% Generate the contents of a simple .app file %% app(Name, Modules) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, "1"}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/rgen1/000077500000000000000000000000001254103774400153275ustar00rootroot00000000000000rebar-2.6.0/inttest/rgen1/reltool.config000066400000000000000000000013151254103774400201760ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et {sys, [ {rel, "rgen1", "0.1", [ kernel, stdlib, sasl, crypto ]}, {rel, "start_clean", "", [ kernel, stdlib ]}, {boot_rel, "rgen1"}, {profile, embedded}, {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)"]}, {excl_archive_filters, [".*"]}, {app, sasl, [{incl_cond, include}]} ]}. {target_dir, "mytarget"}. {overlay_vars, "vars.config"}. {overlay, [ {mkdir, "data"}, {template, "test.config", "etc/test.config"} ]}. rebar-2.6.0/inttest/rgen1/retest.config000066400000000000000000000000231254103774400200170ustar00rootroot00000000000000{timeout, 120000}. rebar-2.6.0/inttest/rgen1/rgen1_rt.erl000066400000000000000000000006441254103774400175600ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(rgen1_rt). -compile(export_all). %% Exercise release generation w/ templating files() -> [ {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.6.0/inttest/rgen1/test.config000066400000000000000000000000331254103774400174710ustar00rootroot00000000000000{web_port, {{web_port}} }. rebar-2.6.0/inttest/rgen1/vars.config000066400000000000000000000000221254103774400174630ustar00rootroot00000000000000{web_port, 1234}. rebar-2.6.0/inttest/t_custom_config/000077500000000000000000000000001254103774400174755ustar00rootroot00000000000000rebar-2.6.0/inttest/t_custom_config/custom.config000066400000000000000000000000761254103774400222010ustar00rootroot00000000000000{deps, [ {boo, "."} ]}. {erl_opts, [warnings_as_errors]}. rebar-2.6.0/inttest/t_custom_config/custom_config.erl000066400000000000000000000001021254103774400230310ustar00rootroot00000000000000-module(custom_config). -compile(export_all). test() -> ok. rebar-2.6.0/inttest/t_custom_config/t_custom_config_rt.erl000066400000000000000000000025321254103774400240720ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -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 -vv", [{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,false\\}\\]", [{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.6.0/inttest/tdeps1/000077500000000000000000000000001254103774400155135ustar00rootroot00000000000000rebar-2.6.0/inttest/tdeps1/a.erl000066400000000000000000000001621254103774400164360ustar00rootroot00000000000000-module(a). -compile(export_all). -include_lib("b/include/b.hrl"). hello() -> io:format("~s\n", [?HELLO]). rebar-2.6.0/inttest/tdeps1/a.rebar.config000066400000000000000000000000501254103774400202070ustar00rootroot00000000000000{deps, [{b, "1", {git, "../repo/b"}}]}. rebar-2.6.0/inttest/tdeps1/b.hrl000066400000000000000000000000411254103774400164360ustar00rootroot00000000000000-include_lib("c/include/c.hrl"). rebar-2.6.0/inttest/tdeps1/b.rebar.config000066400000000000000000000000501254103774400202100ustar00rootroot00000000000000{deps, [{c, "1", {git, "../repo/c"}}]}. rebar-2.6.0/inttest/tdeps1/c.hrl000066400000000000000000000000271254103774400164430ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.6.0/inttest/tdeps1/tdeps1_rt.erl000066400000000000000000000034201254103774400201230ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -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"} ]. apply_cmds([], _Params) -> ok; apply_cmds([Cmd | Rest], Params) -> io:format("Running: ~s (~p)\n", [Cmd, Params]), {ok, _} = retest_sh:run(Cmd, Params), apply_cmds(Rest, Params). run(_Dir) -> %% Initialize the b/c apps as git repos so that dependencies pull %% properly GitCmds = ["git init", "git add -A", "git config user.email 'tdeps@example.com'", "git config user.name 'tdeps'", "git commit -a -m 'Initial Commit'"], apply_cmds(GitCmds, [{dir, "repo/b"}]), apply_cmds(GitCmds, [{dir, "repo/c"}]), {ok, _} = retest_sh:run("./rebar get-deps", []), {ok, _} = retest_sh:run("./rebar 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.6.0/inttest/tdeps2/000077500000000000000000000000001254103774400155145ustar00rootroot00000000000000rebar-2.6.0/inttest/tdeps2/a.erl000066400000000000000000000000671254103774400164430ustar00rootroot00000000000000-module({{module}}). -include_lib("b/include/b.hrl"). rebar-2.6.0/inttest/tdeps2/a.rebar.config000066400000000000000000000000501254103774400202100ustar00rootroot00000000000000{deps, [{b, "1", {git, "../repo/b"}}]}. rebar-2.6.0/inttest/tdeps2/b.hrl000066400000000000000000000000411254103774400164370ustar00rootroot00000000000000-include_lib("c/include/c.hrl"). rebar-2.6.0/inttest/tdeps2/b.rebar.config000066400000000000000000000000501254103774400202110ustar00rootroot00000000000000{deps, [{c, "1", {git, "../repo/c"}}]}. rebar-2.6.0/inttest/tdeps2/c.hrl000066400000000000000000000000271254103774400164440ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.6.0/inttest/tdeps2/root.rebar.config000066400000000000000000000000601254103774400207540ustar00rootroot00000000000000{sub_dirs, ["apps/a1", "apps/a2"]}. rebar-2.6.0/inttest/tdeps2/tdeps2_rt.erl000066400000000000000000000041711254103774400201310ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -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"} ]. apply_cmds([], _Params) -> ok; apply_cmds([Cmd | Rest], Params) -> io:format("Running: ~s (~p)\n", [Cmd, Params]), {ok, _} = retest_sh:run(Cmd, Params), apply_cmds(Rest, Params). run(_Dir) -> %% Initialize the b/c apps as git repos so that dependencies pull %% properly GitCmds = ["git init", "git add -A", "git config user.email 'tdeps@example.com'", "git config user.name 'tdeps'", "git commit -a -m 'Initial Commit'"], ok = apply_cmds(GitCmds, [{dir, "repo/b"}]), ok = apply_cmds(GitCmds, [{dir, "repo/c"}]), {ok, _} = retest_sh:run("./rebar -v get-deps", []), {ok, _} = retest_sh:run("./rebar -v 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.6.0/inttest/tdeps3/000077500000000000000000000000001254103774400155155ustar00rootroot00000000000000rebar-2.6.0/inttest/tdeps3/a.erl000066400000000000000000000001031254103774400164330ustar00rootroot00000000000000-module({{module}}). -include_lib("{{dep}}/include/{{dep}}.hrl"). rebar-2.6.0/inttest/tdeps3/a.rebar.config000066400000000000000000000001121254103774400202100ustar00rootroot00000000000000{deps, [ {b, "1", {git, "../repo/b"}}, {f, "1", {git, "../repo/f"}} ]}. rebar-2.6.0/inttest/tdeps3/b.hrl000066400000000000000000000000411254103774400164400ustar00rootroot00000000000000-include_lib("c/include/c.hrl"). rebar-2.6.0/inttest/tdeps3/b.rebar.config000066400000000000000000000001001254103774400202060ustar00rootroot00000000000000{deps, [ {c, "1", {git, "../repo/c"}} ]}. {lib_dirs, [apps]}. rebar-2.6.0/inttest/tdeps3/c.hrl000066400000000000000000000000411254103774400164410ustar00rootroot00000000000000-include_lib("d/include/d.hrl"). rebar-2.6.0/inttest/tdeps3/c.rebar.config000066400000000000000000000000501254103774400202130ustar00rootroot00000000000000{deps, [{d, "1", {git, "../repo/d"}}]}. rebar-2.6.0/inttest/tdeps3/d.hrl000066400000000000000000000000411254103774400164420ustar00rootroot00000000000000-include_lib("e/include/e.hrl"). rebar-2.6.0/inttest/tdeps3/d.rebar.config000066400000000000000000000000501254103774400202140ustar00rootroot00000000000000{deps, [{e, "1", {git, "../repo/e"}}]}. rebar-2.6.0/inttest/tdeps3/e.hrl000066400000000000000000000000271254103774400164470ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.6.0/inttest/tdeps3/f.hrl000066400000000000000000000000411254103774400164440ustar00rootroot00000000000000-include_lib("e/include/e.hrl"). rebar-2.6.0/inttest/tdeps3/root.rebar.config000066400000000000000000000000301254103774400207520ustar00rootroot00000000000000{sub_dirs, ["apps/a"]}. rebar-2.6.0/inttest/tdeps3/tdeps3_rt.erl000066400000000000000000000057661254103774400201460ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(tdeps3_rt). -compile(export_all). %% Exercise transitive dependencies where there are multiple files %% depending on the same set of deps as well as lib_dir directives %% A -> B -> C -> D -> E %% |--> G(via lib_dir) %% |--> F -> D -> E files() -> [ %% A1 application {create, "ebin/a.app", app(a, [a])}, {template, "a.erl", "src/a.erl", dict:from_list([{module, a}, {dep, b}])}, {copy, "a.rebar.config", "rebar.config"}, {copy, "../../rebar", "rebar"}, %% B application {create, "repo/b/ebin/b.app", app(b, [b])}, {template, "a.erl", "repo/b/src/b.erl", dict:from_list([{module, b}, {dep, 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, [c])}, {template, "a.erl", "repo/c/src/c.erl", dict:from_list([{module, c}, {dep, d}])}, {copy, "c.rebar.config", "repo/c/rebar.config"}, {copy, "c.hrl", "repo/c/include/c.hrl"}, %% D application {create, "repo/d/ebin/d.app", app(d, [d])}, {template, "a.erl", "repo/d/src/d.erl", dict:from_list([{module, d}, {dep, e}])}, {copy, "d.rebar.config", "repo/d/rebar.config"}, {copy, "d.hrl", "repo/d/include/d.hrl"}, %% E application {create, "repo/e/ebin/e.app", app(e, [])}, {copy, "e.hrl", "repo/e/include/e.hrl"}, %% F application {create, "repo/f/ebin/f.app", app(f, [f])}, {template, "a.erl", "repo/f/src/f.erl", dict:from_list([{module, f}, {dep, d}])}, {copy, "c.rebar.config", "repo/f/rebar.config"}, {copy, "f.hrl", "repo/f/include/f.hrl"}, %% G application, which is part of the B repo, in a lib_dir {create, "repo/b/apps/g/ebin/g.app", app(g, [])}, {copy, "e.hrl", "repo/b/apps/g/include/g.hrl"} ]. apply_cmds([], _Params) -> ok; apply_cmds([Cmd | Rest], Params) -> io:format("Running: ~s (~p)\n", [Cmd, Params]), {ok, _} = retest_sh:run(Cmd, Params), apply_cmds(Rest, Params). run(_Dir) -> %% Initialize the b/c apps as git repos so that dependencies pull %% properly GitCmds = ["git init", "git add -A", "git config user.email 'tdeps@example.com'", "git config user.name 'tdeps'", "git commit -a -m 'Initial Commit'"], ok = apply_cmds(GitCmds, [{dir, "repo/b"}]), ok = apply_cmds(GitCmds, [{dir, "repo/c"}]), ok = apply_cmds(GitCmds, [{dir, "repo/d"}]), ok = apply_cmds(GitCmds, [{dir, "repo/e"}]), ok = apply_cmds(GitCmds, [{dir, "repo/f"}]), {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.6.0/inttest/tdeps_update/000077500000000000000000000000001254103774400167745ustar00rootroot00000000000000rebar-2.6.0/inttest/tdeps_update/a.erl000066400000000000000000000000671254103774400177230ustar00rootroot00000000000000-module({{module}}). -include_lib("b/include/b.hrl"). rebar-2.6.0/inttest/tdeps_update/a.rebar.config000066400000000000000000000000741254103774400214760ustar00rootroot00000000000000{deps, [{b, "0.2.3", {git, "../repo/b", {tag, "0.2.3"}}}]}. rebar-2.6.0/inttest/tdeps_update/a2.rebar.config000066400000000000000000000000741254103774400215600ustar00rootroot00000000000000{deps, [{b, "0.2.4", {git, "../repo/b", {tag, "0.2.4"}}}]}. rebar-2.6.0/inttest/tdeps_update/a3.rebar.config000066400000000000000000000000741254103774400215610ustar00rootroot00000000000000{deps, [{b, "0.2.5", {git, "../repo/b", {tag, "0.2.5"}}}]}. rebar-2.6.0/inttest/tdeps_update/a4.rebar.config000066400000000000000000000002031254103774400215540ustar00rootroot00000000000000{deps, [ {b, "0.2.6", {git, "../repo/b", {tag, "0.2.6"}}}, {f, "0.1", {git, "../repo/f", {tag, "0.1"}}} ]}. rebar-2.6.0/inttest/tdeps_update/b.hrl000066400000000000000000000000411254103774400177170ustar00rootroot00000000000000-include_lib("c/include/c.hrl"). rebar-2.6.0/inttest/tdeps_update/b.rebar.config000066400000000000000000000000701254103774400214730ustar00rootroot00000000000000{deps, [{c, "1.0", {git, "../repo/c", {tag, "1.0"}}}]}. rebar-2.6.0/inttest/tdeps_update/b2.rebar.config000066400000000000000000000000701254103774400215550ustar00rootroot00000000000000{deps, [{c, "1.1", {git, "../repo/c", {tag, "1.1"}}}]}. rebar-2.6.0/inttest/tdeps_update/b3.rebar.config000066400000000000000000000000701254103774400215560ustar00rootroot00000000000000{deps, [{c, "1.2", {git, "../repo/c", {tag, "1.2"}}}]}. rebar-2.6.0/inttest/tdeps_update/b4.rebar.config000066400000000000000000000000701254103774400215570ustar00rootroot00000000000000{deps, [{c, "1.3", {git, "../repo/c", {tag, "1.3"}}}]}. rebar-2.6.0/inttest/tdeps_update/c.hrl000066400000000000000000000000271254103774400177240ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.6.0/inttest/tdeps_update/c.rebar.config000066400000000000000000000000701254103774400214740ustar00rootroot00000000000000{deps, [{d, "0.7", {git, "../repo/d", {tag, "0.7"}}}]}. rebar-2.6.0/inttest/tdeps_update/c2.hrl000066400000000000000000000000411254103774400200020ustar00rootroot00000000000000-include_lib("d/include/d.hrl"). rebar-2.6.0/inttest/tdeps_update/c2.rebar.config000066400000000000000000000001641254103774400215620ustar00rootroot00000000000000{deps, [ {d, "0.7", {git, "../repo/d", {tag, "0.7"}}}, {e, "2.0", {git, "../repo/e", {tag, "2.0"}}} ]}. rebar-2.6.0/inttest/tdeps_update/c3.rebar.config000066400000000000000000000001641254103774400215630ustar00rootroot00000000000000{deps, [ {d, "0.7", {git, "../repo/d", {tag, "0.7"}}}, {e, "2.1", {git, "../repo/e", {tag, "2.1"}}} ]}. rebar-2.6.0/inttest/tdeps_update/d.hrl000066400000000000000000000000271254103774400177250ustar00rootroot00000000000000-define(HELLO, hello). rebar-2.6.0/inttest/tdeps_update/root.rebar.config000066400000000000000000000000311254103774400222320ustar00rootroot00000000000000{sub_dirs, ["apps/a1"]}. rebar-2.6.0/inttest/tdeps_update/tdeps_update_rt.erl000066400000000000000000000127441254103774400226760ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(tdeps_update_rt). -compile(export_all). %% Exercises update deps, with recursive dependency updates. %% Initially: %% A(v0.5) -> B(v0.2.3) -> C(v1.0) %% But after updating A to 0.6: %% A(v0.6) -> B(v0.2.4) -> C(v1.1) %% -> D(v0.7) %% And after updating A to 0.7: %% A(v0.7) -> B(v0.2.5) -> C(v1.2) -> E(v2.0) %% -> D(v0.7) %% And after updating A to 0.8: %% A(v0.8) -> B(v0.2.6) -> C(v1.3) -> E(v2.1) %% -> D(v0.7) %% -> F(v0.1) -> E(v2.1) files() -> [ %% A1 application {create, "apps/a1/ebin/a1.app", app(a1, [a1], "0.5")}, {copy, "a.rebar.config", "apps/a1/rebar.config"}, {template, "a.erl", "apps/a1/src/a1.erl", dict:from_list([{module, a1}])}, {copy, "root.rebar.config", "rebar.config"}, {copy, "../../rebar", "rebar"}, %% B application {create, "repo/b/ebin/b.app", app(b, [], "0.2.3")}, {create, "b2.app", app(b, [], "0.2.4")}, {create, "b3.app", app(b, [], "0.2.5")}, {create, "b4.app", app(b, [], "0.2.6")}, {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, [], "1.0")}, {create, "c2.app", app(c, [], "1.1")}, {create, "c3.app", app(c, [], "1.2")}, {create, "c4.app", app(c, [], "1.3")}, {copy, "c.hrl", "repo/c/include/c.hrl"}, %% D application {create, "repo/d/ebin/d.app", app(d, [], "0.7")}, {copy, "d.hrl", "repo/d/include/d.hrl"}, %% E application {create, "repo/e/ebin/e.app", app(e, [], "2.0")}, {create, "e2.app", app(e, [], "2.1")}, %% F application {create, "repo/f/ebin/f.app", app(f, [], "0.1")}, %% update files {copy, "a2.rebar.config", "a2.rebar.config"}, {copy, "a3.rebar.config", "a3.rebar.config"}, {copy, "a4.rebar.config", "a4.rebar.config"}, {copy, "b2.rebar.config", "b2.rebar.config"}, {copy, "b3.rebar.config", "b3.rebar.config"}, {copy, "b4.rebar.config", "b4.rebar.config"}, {copy, "c2.hrl", "c2.hrl"}, {copy, "c.rebar.config", "c.rebar.config"}, {copy, "c2.rebar.config", "c2.rebar.config"}, {copy, "c3.rebar.config", "c3.rebar.config"} ]. apply_cmds([], _Params) -> ok; apply_cmds([Cmd | Rest], Params) -> io:format("Running: ~s (~p)\n", [Cmd, Params]), {ok, _} = retest_sh:run(Cmd, Params), apply_cmds(Rest, Params). run(_Dir) -> %% Initialize the b/c/d apps as git repos so that dependencies pull %% properly GitCmds = ["git init", "git add -A", "git config user.email 'tdeps@example.com'", "git config user.name 'tdeps'", "git commit -a -m 'Initial Commit'"], BCmds = ["git tag 0.2.3", "cp ../../b2.rebar.config rebar.config", "cp ../../b2.app ebin/b.app", "git commit -a -m 'update to 0.2.4'", "git tag 0.2.4", "cp ../../b3.rebar.config rebar.config", "cp ../../b3.app ebin/b.app", "git commit -a -m 'update to 0.2.5'", "git tag 0.2.5", "cp ../../b4.rebar.config rebar.config", "cp ../../b4.app ebin/b.app", "git commit -a -m 'update to 0.2.6'", "git tag 0.2.6"], %"git checkout 0.2.3"], CCmds = ["git tag 1.0", "cp ../../c2.hrl include/c.hrl", "cp ../../c2.app ebin/c.app", "cp ../../c.rebar.config rebar.config", "git add rebar.config", "git commit -a -m 'update to 1.1'", "git tag 1.1", "cp ../../c3.app ebin/c.app", "cp ../../c2.rebar.config rebar.config", "git commit -a -m 'update to 1.2'", "git tag 1.2", "cp ../../c4.app ebin/c.app", "cp ../../c3.rebar.config rebar.config", "git commit -a -m 'update to 1.3'", "git tag 1.3"], %"git checkout 1.0"], DCmds = ["git tag 0.7"], ECmds = ["git tag 2.0", "cp ../../e2.app ebin/e.app", "git commit -a -m 'update to 2.1'", "git tag 2.1"], FCmds = ["git tag 0.1"], ok = apply_cmds(GitCmds++BCmds, [{dir, "repo/b"}]), ok = apply_cmds(GitCmds++CCmds, [{dir, "repo/c"}]), ok = apply_cmds(GitCmds++DCmds, [{dir, "repo/d"}]), ok = apply_cmds(GitCmds++ECmds, [{dir, "repo/e"}]), ok = apply_cmds(GitCmds++FCmds, [{dir, "repo/f"}]), {ok, _} = retest_sh:run("./rebar -v get-deps", []), {ok, _} = retest_sh:run("./rebar -v compile", []), os:cmd("cp a2.rebar.config apps/a1/rebar.config"), {ok, _} = retest_sh:run("./rebar -v update-deps", []), {ok, _} = retest_sh:run("./rebar -v compile", []), os:cmd("cp a3.rebar.config apps/a1/rebar.config"), {ok, _} = retest_sh:run("./rebar -v update-deps", []), {ok, _} = retest_sh:run("./rebar -v compile", []), os:cmd("cp a4.rebar.config apps/a1/rebar.config"), {ok, _} = retest_sh:run("./rebar -v update-deps", []), {ok, _} = retest_sh:run("./rebar -v compile", []), ok. %% %% Generate the contents of a simple .app file %% app(Name, Modules, Version) -> App = {application, Name, [{description, atom_to_list(Name)}, {vsn, Version}, {modules, Modules}, {registered, []}, {applications, [kernel, stdlib]}]}, io_lib:format("~p.\n", [App]). rebar-2.6.0/inttest/thooks/000077500000000000000000000000001254103774400156225ustar00rootroot00000000000000rebar-2.6.0/inttest/thooks/fish.erl000066400000000000000000000000671254103774400172620ustar00rootroot00000000000000-module(fish). -compile(export_all). fish() -> fish. rebar-2.6.0/inttest/thooks/rebar.config000066400000000000000000000004221254103774400201020ustar00rootroot00000000000000%% 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.6.0/inttest/thooks/thooks_rt.erl000066400000000000000000000023671254103774400203520ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -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.6.0/inttest/tplugins/000077500000000000000000000000001254103774400161605ustar00rootroot00000000000000rebar-2.6.0/inttest/tplugins/bad.config000066400000000000000000000000661254103774400200770ustar00rootroot00000000000000{plugins, [bad_plugin]}. {plugin_dir, "bad_plugins"}. rebar-2.6.0/inttest/tplugins/bad_plugin.erl000066400000000000000000000002341254103774400207670ustar00rootroot00000000000000-module(bad_plugin). -compile(export_all). %% this plugin contains numerous DELIBERATE syntax errors fwibble(Config, _) > file:delete("fwibble.test") rebar-2.6.0/inttest/tplugins/fish.erl000066400000000000000000000000671254103774400176200ustar00rootroot00000000000000-module(fish). -compile(export_all). fish() -> fish. rebar-2.6.0/inttest/tplugins/rebar.config000066400000000000000000000000321254103774400204350ustar00rootroot00000000000000{plugins, [test_plugin]}. rebar-2.6.0/inttest/tplugins/test_plugin.erl000066400000000000000000000004321254103774400212200ustar00rootroot00000000000000-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.6.0/inttest/tplugins/tplugins_rt.erl000066400000000000000000000025531254103774400212430ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -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.6.0/pr2relnotes.py000077500000000000000000000127411254103774400154620ustar00rootroot00000000000000#!/usr/bin/env python ## Install info ## $ virtualenv env ## $ source env/bin/activate ## $ pip install PyGithub ## ## Examples: ## Find the differences from last tag to current ## $ pr2relnotes.py alpha-6 HEAD import argparse import re import os import subprocess from github import Github from github import GithubException def dprint(*args): if VERBOSE: print str(args) def get_args(): """ Get command line arguments """ parser = argparse.ArgumentParser(description="Find the PR's between two versions") parser.add_argument("old", help = "old version to use") parser.add_argument("new", help = "new version to use") parser.add_argument("-v", "--verbose", help="Enable debug output", default=False, action="store_true") parser.add_argument("-f", "--file", help="Output file to store results (default: tagdiff.md)", default="tagdiff.md") return parser.parse_args() def search_prs(log): """ Search lines of text for PR numbers """ # Find all matches using regex iterator, using the PR # as the group match resultlist = [str(m.group(1)) for m in re.finditer(r"erge pull request #(\d+)", log)] return sorted(resultlist) def get_env(env): return os.environ[env] def get_formatted_issue(repo, issue, title, url): """ Single place to adjust formatting output of PR data """ # Newline support writelines() call which doesn't add newlines # on its own return("* {}/{}: [{}]({})\n".format(repo, issue, title, url)) def gh_get_issue_output(org, repo, issuenum): """ Look up PR information using the GitHub api """ # Attempt to look up the PR, and don't take down the whole # shebang if a API call fails # This will fail often on forks who don't have the # PRs numbers associated with the forked account # Return empty string on error try: repoObj = gh.get_repo(org + "/" + repo) issue = repoObj.get_issue(int(issuenum)) title = issue.title html_url = issue.html_url except GithubException as e: print "Github error({0}): {1}".format(e.status, e.data) return "" except: print "Some github error" return "" return(get_formatted_issue(repo, issuenum, title, html_url)) def get_org(repourl): """ Simple function to parse the organization out of a GitHub URL """ dprint("Current repourl to search: " + repourl) # GitHub URLs can be: # http[s]://www.github.com/org/repo # or git@github.com:/org/repo pattern = re.compile(r"github.com[/:]+(\w+)/") m = re.search(pattern, repourl) # Fail fast if this is wrong so we can add a pattern to the search if m: return m.group(1) else: raise Exception("Incorrect regex pattern finding repo org") def get_name(repourl): """ Simple function to parse the repository name out of a GitHub URL """ dprint("Current repourl to search: " + repourl) repo_pattern = re.compile(r"github.com[/:]\w+/(\w+)") m = re.search(repo_pattern, repourl) if m: return m.group(1) else: raise Exception("Incorrect rexex pattern finding repo url") def get_repo_url_from_remote(): """ Function that gets the repository URL from the `git remote` listing """ git_remote_bytes = subprocess.check_output(["git", "remote", "-v"]) # check_output returns the command results in raw byte format remote_string = git_remote_bytes.decode('utf-8') pattern = re.compile(r"github.com[/:]\w+/\w+") m = re.search(pattern, remote_string) if m: return m.group(0) else: raise Exception("Incorrect rexex pattern finding repo url") def process_log(gitlog, repo_url): """ Handles the processing of the gitlog and returns a list of PRs already formatted for output """ pr_list = search_prs(gitlog) repoorg = get_org(repo_url) reponame = get_name(repo_url) pr_buffer = [] for issue in pr_list: pr_buffer.append(gh_get_issue_output(repoorg, reponame, issue)) return pr_buffer def fetch_log(old_ver, new_ver): """ Function that processes the git log between the old and new versions """ dprint("Current working directory", os.getcwd()) gitlogbytes = subprocess.check_output(["git", "log", str(old_ver + ".." + new_ver)]) return gitlogbytes.decode('utf-8') def compare_versions(repo_url, old_ver, new_ver): # Formatted list of all PRs for all repos pr_out = [] gitlog = fetch_log(old_ver, new_ver) pr_out.extend(process_log(gitlog, repo_url)) return pr_out def main(): args = get_args() # Setup the GitHub object for later use global gh gh = Github(get_env("GHAUTH")) if gh == "": raise Exception("Env var GHAUTH must be set to a valid GitHub API key") if args.verbose: global VERBOSE VERBOSE=True dprint("Inspecting difference in between: ", args.old, " and ", args.new) # Find the github URL of the repo we are operating on repo_url = get_repo_url_from_remote() # Compare old and new versions pr_list = compare_versions(repo_url, args.old, args.new) # Writeout PR listing print "Writing output to file %s" % args.file with open(args.file, 'w') as output: output.writelines(pr_list) if __name__ == "__main__": VERBOSE=False gh=None topdir=os.getcwd() main() rebar-2.6.0/priv/000077500000000000000000000000001254103774400136015ustar00rootroot00000000000000rebar-2.6.0/priv/shell-completion/000077500000000000000000000000001254103774400170575ustar00rootroot00000000000000rebar-2.6.0/priv/shell-completion/bash/000077500000000000000000000000001254103774400177745ustar00rootroot00000000000000rebar-2.6.0/priv/shell-completion/bash/rebar000066400000000000000000000040021254103774400210060ustar00rootroot00000000000000# 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 -D -j -C -p -k -r" lopts="--help \ --commands \ --verbose \ --force \ --jobs \ --config \ --profile \ --keep-going \ --recursive \ --version" cmdsnvars=" \ build-plt \ check-deps \ clean \ compile \ check-plt \ create \ create-app \ create-lib \ create-node \ ct \ dialyze \ doc \ delete-deps \ escriptize \ eunit \ get-deps \ generate \ generate-appups \ generate-upgrade \ help \ list-deps \ list-templates \ prepare-deps \ qc \ refresh-deps \ shell \ update-deps \ version \ xref \ overlay \ apps= \ case= \ dump_spec=1 \ force=1 \ jobs= \ suites= \ verbose=1 \ appid= \ overlay_vars= \ previous_release= \ profiler= \ nodeid= \ root_dir= \ skip_deps=true \ skip_apps= \ target_dir= \ template= \ template_dir= \ tests=" 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.6.0/priv/shell-completion/fish/000077500000000000000000000000001254103774400200105ustar00rootroot00000000000000rebar-2.6.0/priv/shell-completion/fish/rebar.fish000066400000000000000000000232241254103774400217610ustar00rootroot00000000000000## fish completions for rebar 2.5.0 function __fish_rebar_needs_command set cmd (commandline -opc) if [ (count $cmd) -eq 1 -a $cmd[1] = 'rebar' -o $cmd[1] = './rebar' ] return 0 end return 1 end function __fish_rebar_using_command set cmd (commandline -opc) if [ (count $cmd) -gt 1 ] if [ $argv[1] = $cmd[2] ] return 0 end end return 1 end ## Rebar Command Output ## ➜ ~ rebar --version ## rebar 2.5.0 R16B02 20140716_213805 git 2.5.0-14-g1e7c742 ## ➜ ~ rebar --help ## Usage: rebar [-h] [-c] [-v ] [-q ] [-V] [-f] ## [-D ] [-j ] [-C ] [-p] [-k] ## [-r ] [var=value,...] ## ## -h, --help Show the program options ## -c, --commands Show available commands ## -v, --verbose Verbosity level (-v, -vv) ## -q, --quiet Quiet, only print error messages ## -V, --version Show version information ## -f, --force Force ## -D Define compiler macro ## -j, --jobs Number of concurrent workers a command may use. ## Default: 3 ## -C, --config Rebar config file to use ## -p, --profile Profile this run of rebar ## -k, --keep-going Keep running after a command fails ## -r, --recursive Apply commands to subdirs and dependencies ## var=value rebar global variables (e.g. force=1) ## command Command to run (e.g. compile) ## ## To see a list of built-in commands, execute rebar -c. ## ## Type 'rebar help ' for help on specific commands. # general options complete -f -c rebar -n 'not __fish_rebar_needs_command' -l help -d 'Display the manual of a rebar command' ## Flags complete -c rebar -s h -l help -d "Show the program options" complete -c rebar -s c -l commands -d "Show available commands" complete -c rebar -s v -l verbose -d "Verbosity level (-v, -vv, -vvv, --verbose 3). Default: 0" complete -c rebar -s vv -d "Verbosity level 2" complete -c rebar -s vvv -d "Verbosity level 2" complete -c rebar -s q -l quiet -d "Quiet, only print error messages" complete -c rebar -s V -l version -d "Show version information" complete -c rebar -s f -l force -d "Force" complete -c rebar -s D -d "Define compiler macro" complete -c rebar -s j -l jobs -d "Number of concurrent workers a command may use. Default: 3" complete -c rebar -s C -l config -d "Rebar config file to use" complete -c rebar -s p -l profile -d "Profile this run of rebar" complete -c rebar -s k -l keep-going -d "Keep running after a command fails" complete -c rebar -s r -l recursive -d "Apply commands to subdirs and dependencies" ## Not going to cover abbreviations, since this is for completions anyway :D ## rebar allows you to abbreviate the command to run: ## $ rebar co # same as rebar compile ## $ rebar eu # same as rebar eunit ## $ rebar g-d # same as rebar get-deps ## $ rebar x eu # same as rebar xref eunit ## $ rebar l-d # same as rebar list-deps ## $ rebar l-d l-t # same as rebar list-deps list-templates ## $ rebar list-d l-te # same as rebar list-deps list-templates ## ## Core command line options: ## apps=app1,app2 (specify apps to process) ## skip_apps=app1,app2 (specify apps to skip) ## COMMANDS! ## ➜ ~ rebar -c ## clean Clean complete -f -c rebar -n '__fish_rebar_needs_command' -a clean -d 'Clean' ## compile Compile sources complete -f -c rebar -n '__fish_rebar_needs_command' -a compile -d 'Compile sources' ## escriptize Generate escript archive complete -f -c rebar -n '__fish_rebar_needs_command' -a escriptize -d 'Generate escript archive' ## create template= [var=foo,...] Create skel based on template and vars complete -f -c rebar -n '__fish_rebar_needs_command' -a create -d 'Create skel based on template and vars' complete -f -c rebar -n '__fish_rebar_using_command create' -a 'template=' -d 'Template name' ## create-app [appid=myapp] Create simple app skel complete -f -c rebar -n '__fish_rebar_needs_command' -a create-app -d 'Create simple app skel' complete -f -c rebar -n '__fish_rebar_using_command create-app' -a 'appid=' -d 'Application name' ## create-lib [libid=mylib] Create simple lib skel complete -f -c rebar -n '__fish_rebar_needs_command' -a create-lib -d 'Create simple lib skel' complete -f -c rebar -n '__fish_rebar_using_command create-lib' -a 'libid=' -d 'Library name' ## create-node [nodeid=mynode] Create simple node skel complete -f -c rebar -n '__fish_rebar_needs_command' -a create-node -d 'Create simple node skel' complete -f -c rebar -n '__fish_rebar_using_command create-node' -a 'nodeid=' -d 'Node name' ## list-templates List available templates complete -f -c rebar -n '__fish_rebar_needs_command' -a list-templates -d 'List available templates' ## doc Generate Erlang program documentation complete -f -c rebar -n '__fish_rebar_needs_command' -a doc -d 'Generate Erlang program documentation' ## prepare-deps Run 'rebar -r get-deps compile' complete -f -c rebar -n '__fish_rebar_needs_command' -a prepare-deps -d 'Prepare Dependencies' ## refresh-deps Run 'rebar -r update-deps compile' complete -f -c rebar -n '__fish_rebar_needs_command' -a refresh-deps -d 'Refresh Dependencies' ## check-deps Display to be fetched dependencies complete -f -c rebar -n '__fish_rebar_needs_command' -a check-deps -d 'Display to be fetched dependencies' ## get-deps Fetch dependencies complete -f -c rebar -n '__fish_rebar_needs_command' -a get-deps -d 'Fetch dependencies' ## update-deps Update fetched dependencies complete -f -c rebar -n '__fish_rebar_needs_command' -a update-deps -d 'Update fetched dependencies' ## delete-deps Delete fetched dependencies complete -f -c rebar -n '__fish_rebar_needs_command' -a delete-deps -d 'Delete fetched dependencies' ## list-deps List dependencies complete -f -c rebar -n '__fish_rebar_needs_command' -a list-deps -d 'List Dependencies' ## generate [dump_spec=0/1] Build release with reltool complete -f -c rebar -n '__fish_rebar_needs_command' -a generate -d 'Build release with reltool' complete -f -c rebar -n '__fish_rebar_using_command generate' -a 'dump_spec=0 dump_spec=1' ## overlay Run reltool overlays only complete -f -c rebar -n '__fish_rebar_needs_command' -a overlay -d 'Run reltool overlays only' ## generate-upgrade previous_release=path Build an upgrade package complete -f -c rebar -n '__fish_rebar_needs_command' -a generate-upgrade -d 'Build an upgrade package' complete -f -c rebar -n '__fish_rebar_using_command generate-upgrade' -a 'previous_release=' ## generate-appups previous_release=path Generate appup files complete -f -c rebar -n '__fish_rebar_needs_command' -a generate-appups -d 'Generate appup files' complete -f -c rebar -n '__fish_rebar_using_command generate-appups' -a 'previous_release=' ## eunit [suite[s]=foo] Run EUnit tests in foo.erl and ## test/foo_tests.erl ## [suite[s]=foo] [test[s]=bar] Run specific EUnit tests [first test ## name starting with 'bar' in foo.erl ## and test/foo_tests.erl] ## [test[s]=bar] For every existing suite, run the first ## test whose name starts with bar and, if ## no such test exists, run the test whose ## name starts with bar in the suite's ## _tests module. ## [random_suite_order=true] Run tests in a random order, either ## [random_suite_order=Seed] with a random seed for the PRNG, or a ## specific one. complete -f -c rebar -n '__fish_rebar_needs_command' -a eunit -d 'Run EUnit tests' complete -f -c rebar -n '__fish_rebar_using_command eunit' -a 'suites=' complete -f -c rebar -n '__fish_rebar_using_command eunit' -a 'tests=' complete -f -c rebar -n '__fish_rebar_using_command eunit' -a 'random_suite_order=' ## ct [suite[s]=] [case=] Run common_test suites complete -f -c rebar -n '__fish_rebar_needs_command' -a ct -d 'Run common_test suites' complete -f -c rebar -n '__fish_rebar_using_command ct' -a 'suites=' complete -f -c rebar -n '__fish_rebar_using_command ct' -a 'case=' ## qc Test QuickCheck properties complete -f -c rebar -n '__fish_rebar_needs_command' -a qc -d 'Test QuickCheck properties' ## xref Run cross reference analysis complete -f -c rebar -n '__fish_rebar_needs_command' -a xref -d 'Run cross reference analysis' ## shell Start a shell similar to ## 'erl -pa ebin -pa deps/*/ebin' complete -f -c rebar -n '__fish_rebar_needs_command' -a shell -d 'Start a shell' ## help Show the program options complete -f -c rebar -n '__fish_rebar_needs_command' -a help -d 'Show the program options' ## version Show version information complete -f -c rebar -n '__fish_rebar_needs_command' -a version -d 'Show version information' rebar-2.6.0/priv/shell-completion/zsh/000077500000000000000000000000001254103774400176635ustar00rootroot00000000000000rebar-2.6.0/priv/shell-completion/zsh/_rebar000066400000000000000000000074171254103774400210510ustar00rootroot00000000000000#compdef rebar local curcontext=$curcontext state ret=1 typeset -ga _rebar_global_opts _rebar_global_opts=( '(--help -h)'{--help,-h}'[Show the program options]' '(--commands -c)'{--commands,-c}'[Show available commands]' '(--version -V)'{--version,-V}'[Show version information]' '(-vv -v)'--verbose'[Enforce verbosity level]' '(-vv)-v[Slightly more verbose output]' '(-v)-vv[More verbose output]' '(-vv -v --verbose)'{--quiet,-q}'[Quiet, only print error messages]' '(--force -f)'{--force,-f}'[Force]' '-D+[Define compiler macro]' '(--jobs -j)'{--jobs+,-j+}'[Number of concurrent workers a command may use. Default: 3]:workers:(1 2 3 4 5 6 7 8 9)' '(--config -C)'{--config,-C}'[Rebar config file to use]:files:_files' '(--profile -p)'{--profile,-p}'[Profile this run of rebar]' '(--keep-going -k)'{--keep-going,-k}'[Keep running after a command fails]' '(--recursive -r)'{--recursive,-r}'[Apply commands to subdirs and dependencies]' ) _rebar () { _arguments -C $_rebar_global_opts \ '*::command and variable:->cmd_and_var' \ && return case $state in cmd_and_var) _values -S = 'variables' \ 'dialyze[Analyze the code for discrepancies]' \ 'build-plt[Build project-specific PLT]' \ 'check-plt[Check the plt for consistency and rebuild it if it is not up-to-date]' \ 'clean[Clean]' \ 'compile[Compile sources]' \ 'create[Create skel based on template and vars]' \ 'create-app[Create simple app skel]' \ 'create-lib[Create simple lib skel]' \ 'create-node[Create simple node skel]' \ 'list-template[List available templates]' \ 'doc[Generate Erlang program documentation]' \ 'check-deps[Display to be fetched dependencies]' \ 'prepare-deps[Fetch and build dependencies]' \ 'refresh-deps[Update and build dependencies]' \ 'get-deps[Fetch dependencies]' \ 'update-deps[Update fetched dependencies]' \ 'delete-deps[Delete fetched dependencies]' \ 'list-deps[List dependencies]' \ 'generate[Build release with reltool]' \ 'overlay[Run reltool overlays only]' \ 'generate-appups[Generate appup files]' \ 'generate-upgrade[Build an upgrade package]' \ 'escriptize[Create stand-alone escript executable]' \ 'eunit[Run eunit tests]' \ 'ct[Run common_test suites]' \ 'qc[Test QuickCheck properties]' \ 'xref[Run cross reference analysis]' \ 'help[Show the program options]' \ 'version[Show version information]' \ 'apps[Application names to process]:' \ 'case[Common Test case]:' \ 'dump_spec[Dump reltool spec]::flag:(1)' \ 'jobs[Number of workers]::workers:(0 1 2 3 4 5 6 7 8 9)' \ 'suites[Common Test suites]::suite name:_path_files -W "(src test)" -g "*.erl(:r)"' \ 'verbose[Verbosity level]::verbosity level:(0 1 2 3)' \ 'appid[Application id]:' \ 'overlay_vars[Overlay variables file]:' \ 'previous_release[Previous release path]:' \ 'profiler[Select profiler]::flag:(fprof eflame)' \ 'nodeid[Node id]:' \ 'root_dir[Reltool config root directory]::directory:_files -/' \ 'shell[Start a shell similar to erl -pa ebin -pa deps/*/ebin]' \ 'skip_deps[Skip deps]::flag:(true false)' \ 'skip_apps[Application names to not process]::flag:(true false)' \ 'target_dir[Target directory]:' \ 'template[Template name]:' \ 'template_dir[Template directory]::directory:_files -/' \ 'tests[Run eunit tests whose name starts with given string]:' \ && ret=0 ;; esac } _rebar # Local variables: # mode: shell-script # sh-basic-offset: 2 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: sw=2 ts=2 et filetype=sh rebar-2.6.0/priv/templates/000077500000000000000000000000001254103774400155775ustar00rootroot00000000000000rebar-2.6.0/priv/templates/basicnif.c000066400000000000000000000036261254103774400175300ustar00rootroot00000000000000#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.6.0/priv/templates/basicnif.erl000066400000000000000000000017571254103774400200730ustar00rootroot00000000000000-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.6.0/priv/templates/basicnif.template000066400000000000000000000002071254103774400211110ustar00rootroot00000000000000{variables, [{module, "mymodule"}]}. {template, "basicnif.erl", "src/{{module}}.erl"}. {template, "basicnif.c", "c_src/{{module}}.c"}. rebar-2.6.0/priv/templates/ctsuite.erl000066400000000000000000000142021254103774400177620ustar00rootroot00000000000000%% 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.6.0/priv/templates/ctsuite.template000066400000000000000000000001371254103774400210150ustar00rootroot00000000000000{variables, [{testmod, "mymodule"}]}. {template, "ctsuite.erl", "test/{{testmod}}_SUITE.erl"}. rebar-2.6.0/priv/templates/simpleapp.app.src000066400000000000000000000003361254103774400210630ustar00rootroot00000000000000{application, {{appid}}, [ {description, ""}, {vsn, "1"}, {registered, []}, {applications, [ kernel, stdlib ]}, {mod, { {{appid}}_app, []}}, {env, []} ]}. rebar-2.6.0/priv/templates/simpleapp.template000066400000000000000000000003171254103774400213270ustar00rootroot00000000000000{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.6.0/priv/templates/simpleapp_app.erl000066400000000000000000000005531254103774400211400ustar00rootroot00000000000000-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.6.0/priv/templates/simpleapp_sup.erl000066400000000000000000000013131254103774400211620ustar00rootroot00000000000000-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.6.0/priv/templates/simpleevent.erl000066400000000000000000000030171254103774400206370ustar00rootroot00000000000000-module({{eventid}}). -behaviour(gen_event). %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([start_link/0, add_handler/2]). %% ------------------------------------------------------------------ %% gen_event Function Exports %% ------------------------------------------------------------------ -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). -record(state, {}). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ start_link() -> gen_event:start_link({local, ?MODULE}). add_handler(Handler, Args) -> gen_event:add_handler(?MODULE, Handler, Args). %% ------------------------------------------------------------------ %% gen_event Function Definitions %% ------------------------------------------------------------------ init([]) -> {ok, #state{}}. handle_event(_Event, State) -> {ok, State}. handle_call(_Request, State) -> Reply = ok, {ok, Reply, State}. handle_info(_Info, State) -> {ok, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ rebar-2.6.0/priv/templates/simpleevent.template000066400000000000000000000001331254103774400216640ustar00rootroot00000000000000{variables, [{eventid, "myevent"}]}. {template, "simpleevent.erl", "src/{{eventid}}.erl"}. rebar-2.6.0/priv/templates/simplefsm.erl000066400000000000000000000033171254103774400203060ustar00rootroot00000000000000-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.6.0/priv/templates/simplefsm.template000066400000000000000000000001231254103774400213270ustar00rootroot00000000000000{variables, [{fsmid, "myfsm"}]}. {template, "simplefsm.erl", "src/{{fsmid}}.erl"}. rebar-2.6.0/priv/templates/simplelib.app.src000066400000000000000000000004171254103774400210510ustar00rootroot00000000000000{application, {{libid}}, [ {description, "An Erlang {{libid}} library"}, {vsn, "1"}, {modules, [ {{libid}} ]}, {registered, []}, {applications, [ kernel, stdlib ]}, {env, []} ]}. rebar-2.6.0/priv/templates/simplelib.erl000066400000000000000000000002561254103774400202660ustar00rootroot00000000000000-module({{libid}}). %% {{libid}}: {{libid}} library's entry point. -export([my_func/0]). %% API my_func() -> ok(). %% Internals ok() -> ok. %% End of Module. rebar-2.6.0/priv/templates/simplelib.template000066400000000000000000000002151254103774400213120ustar00rootroot00000000000000{variables, [{libid, "mylib"}]}. {template, "simplelib.app.src", "src/{{libid}}.app.src"}. {template, "simplelib.erl", "src/{{libid}}.erl"}. rebar-2.6.0/priv/templates/simplemod.erl000066400000000000000000000001011254103774400202640ustar00rootroot00000000000000-module({{modid}}). -export([my_func/0]). my_func() -> ok. rebar-2.6.0/priv/templates/simplemod.template000066400000000000000000000002221254103774400213210ustar00rootroot00000000000000{variables, [{modid, "mymod"}]}. {template, "simplemod.erl", "src/{{modid}}.erl"}. {template, "simplemod_tests.erl", "test/{{modid}}_tests.erl"}. rebar-2.6.0/priv/templates/simplemod_tests.erl000066400000000000000000000001041254103774400215110ustar00rootroot00000000000000-module({{modid}}_tests). -include_lib("eunit/include/eunit.hrl"). rebar-2.6.0/priv/templates/simplenode.erl.script000066400000000000000000000026211254103774400217460ustar00rootroot00000000000000#!/bin/sh # /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then POSIX_SHELL="true" export POSIX_SHELL exec /usr/bin/ksh $0 "$@" fi # clear it so if we invoke other scripts, they run as ksh as well unset POSIX_SHELL ## 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 -P) # 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.6.0/priv/templates/simplenode.install_upgrade.escript000066400000000000000000000036551254103774400245160ustar00rootroot00000000000000#!/usr/bin/env escript %%! -noshell -noinput %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et %% This file is left for backward-compatibility. %% You, probably, shouldn't include it to new projects. main([NodeName, Cookie, ReleasePackage]) -> io:format("WARNING: 'install_upgrade.escript' is deprecated! " "Use 'nodetool upgrade' instead.~n"), NodeRoot = filename:dirname(filename:dirname(escript:script_name())), NodeTool = which_nodetool(NodeRoot), process_flag(trap_exit, true), Port = erlang:open_port( {spawn_executable, NodeTool}, [{args, ["-sname", NodeName, "-setcookie", Cookie, "upgrade", ReleasePackage]}, binary, exit_status, use_stdio, stderr_to_stdout, hide]), port_loop(Port); main(_) -> halt(1). which_nodetool(NodeRoot) -> %% ${RELEASE_ROOT}/ %% bin/install_upgrade.escript %% bin/nodetool ? %% erts-/bin/nodetool ? %% releases//nodetool ? %% releases/start_erl.data {ok, Content} = file:read_file(filename:join([NodeRoot, "releases", "start_erl.data"])), [ErtsVsn, AppVsn] = binary:split(Content, <<" ">>), Probes = [ filename:join([NodeRoot, "bin", "nodetool"]), filename:join([NodeRoot, <<"erts-", ErtsVsn/binary>>, "bin", "nodetool"]), filename:join([NodeRoot, "releases", AppVsn, "bin", "nodetool"]) ], case lists:dropwhile(fun(Path) -> not filelib:is_regular(Path) end, Probes) of [] -> io:format("ERROR: can't find 'nodetool' in ~p.~n", [Probes]), halt(2); [Path | _] -> Path end. port_loop(Port) -> receive {Port, {data, Data}} -> io:put_chars(Data), port_loop(Port); {Port, {exit_status, Status}} -> halt(Status) end. rebar-2.6.0/priv/templates/simplenode.nodetool000077500000000000000000000202471254103774400215130ustar00rootroot00000000000000#!/usr/bin/env escript %% -*- 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), %% any commands that don't need a running node case RestArgs of ["chkconfig", File] -> case file:consult(File) of {ok, _} -> io:format("ok\n"), halt(0); {error, {Line, Mod, Term}} -> io:format(standard_error, ["Error on line ", file:format_error({Line, Mod, Term}), "\n"], []), halt(1); {error, R} -> io:format(standard_error, ["Error reading config file: ", file:format_error(R), "\n"], []), halt(1) end; _ -> ok end, %% 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; {false,pong} -> io:format("Failed to connect to node ~p .\n", [TargetNode]), halt(1); {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of ["getpid"] -> io:format("~p\n", [list_to_integer(rpc:call(TargetNode, os, getpid, []))]); ["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; ["rpc_infinity", Module, Function | RpcArgs] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), [RpcArgs], infinity) 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; ["eval", Str0] -> Str = string:strip(Str0, right, $.) ++ ".", Bindings = erl_eval:new_bindings(), case rpc:call(TargetNode, erl_eval, exprs, [parse(Str), Bindings], 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); {value, Value, _Bindings} -> io:format("~p\n", [Value]) end; ["upgrade", ReleasePackage] -> %% TODO: This script currently does NOT support slim releases. %% Necessary steps to upgrade a slim release are as follows: %% 1. unpack relup archive manually %% 2. copy releases directory and necessary libraries %% 3. using release_hander:set_unpacked/2 . %% For more details, see https://github.com/rebar/rebar/pull/52 %% and https://github.com/rebar/rebar/issues/202 {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release, [ReleasePackage], 60000), io:format("Unpacked Release ~p\n", [Vsn]), {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, check_install_release, [Vsn], 60000), {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, install_release, [Vsn], 60000), io:format("Installed Release ~p\n", [Vsn]), ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], 60000), io:format("Made Release ~p Permanent\n", [Vsn]); Other -> io:format("Other: ~p\n", [Other]), io:format("Usage: nodetool {chkconfig|getpid|ping|stop|restart|reboot|rpc|rpc_infinity|rpcterms|eval|upgrade}\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. parse(Str) -> {ok, Tokens, _} = erl_scan:string(Str), {ok, Exprs} = erl_parse:parse_exprs(Tokens), Exprs. rebar-2.6.0/priv/templates/simplenode.reltool.config000066400000000000000000000031601254103774400226040ustar00rootroot00000000000000%% -*- mode: erlang -*- %% ex: ft=erlang {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, derived}, {excl_archive_filters, [".*"]}, %% Do not archive built libs {excl_sys_filters, ["^bin/(?!start_clean.boot)", "^erts.*/bin/(dialyzer|typer)", "^erts.*/(doc|info|include|lib|man|src)"]}, {excl_app_filters, ["\.gitignore"]}, {app, {{nodeid}}, [{mod_cond, app}, {incl_cond, include}]} ]}. {target_dir, "{{nodeid}}"}. {overlay, [ {mkdir, "log/sasl"}, {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, {copy, "files/nodetool", "releases/\{\{rel_vsn\}\}/nodetool"}, {copy, "{{nodeid}}/bin/start_clean.boot", "\{\{erts_vsn\}\}/bin/start_clean.boot"}, {copy, "files/{{nodeid}}", "bin/{{nodeid}}"}, {copy, "files/{{nodeid}}.cmd", "bin/{{nodeid}}.cmd"}, {copy, "files/start_erl.cmd", "bin/start_erl.cmd"}, %% Following line may be safely removed in new projects {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.6.0/priv/templates/simplenode.runner000077500000000000000000000267501254103774400212060ustar00rootroot00000000000000#!/bin/sh # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et # /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then POSIX_SHELL="true" export POSIX_SHELL # To support 'whoami' add /usr/ucb to path PATH=/usr/ucb:$PATH export PATH exec /usr/bin/ksh $0 "$@" fi # clear it so if we invoke other scripts, they run as ksh unset POSIX_SHELL RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd -P) RUNNER_SCRIPT=${0##*/} CALLER_DIR=$PWD RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*} RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc # Note the trailing slash on $PIPE_DIR/ PIPE_DIR=/tmp/$RUNNER_BASE_DIR/ RUNNER_USER= WHOAMI=$(whoami) # Make sure this script is running as the appropriate user if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then type sudo > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "sudo doesn't appear to be installed and your EUID isn't $RUNNER_USER" 1>&2 exit 1 fi echo "Attempting to restart script through sudo -H -u $RUNNER_USER" >&2 exec sudo -H -u $RUNNER_USER -i $RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT $@ fi # 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 $CWD/vm.args if exists, otherwise releases/APP_VSN/vm.args, or # else etc/vm.args if [ -e "$CALLER_DIR/vm.args" ]; then VMARGS_PATH=$CALLER_DIR/vm.args USE_DIR=$CALLER_DIR else USE_DIR=$RUNNER_BASE_DIR 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 fi RUNNER_LOG_DIR=$USE_DIR/log # Make sure log directory exists mkdir -p $RUNNER_LOG_DIR # Use releases/VSN/sys.config if it exists otherwise use etc/app.config if [ -e "$USE_DIR/sys.config" ]; then CONFIG_PATH="$USE_DIR/sys.config" else 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 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}'` # Test if REMSH_NAME contains a @ and set REMSH_HOSTNAME_PART # and REMSH_NAME_PART according REMSH_TYPE MAYBE_FQDN_HOSTNAME=`hostname` HOSTNAME=`echo $MAYBE_FQDN_HOSTNAME | awk -F. '{print $1}'` REMSH_HOSTNAME_PART="$MAYBE_FQDN_HOSTNAME" case "$REMSH_NAME" in *@*) REMSH_HOSTNAME_PART=`echo $REMSH_NAME | awk -F@ '{print $2}'` REMSH_NAME_PART=`echo $REMSH_NAME | awk -F@ '{print $1}'` ;; *) REMSH_NAME_PART="$REMSH_NAME" if [ "$REMSH_TYPE" = "-sname" ]; then REMSH_HOSTNAME_PART="$HOSTNAME" else # -name type, check if `hostname` is fqdn if [ "$MAYBE_FQDN_HOSTNAME" = "$HOSTNAME" ]; then echo "Hostname must be a fqdn domain name when node is configured with long names" echo "and the full node name isn't configured in vm.args" exit 1 fi fi ;; esac # Note the `date +%s`, used to allow multiple remsh to the same node # transparently REMSH_NAME_ARG="$REMSH_TYPE remsh`date +%s`@$REMSH_HOSTNAME_PART" REMSH_REMSH_ARG="-remsh $REMSH_NAME_PART@$REMSH_HOSTNAME_PART" # 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 # Make sure CWD is set to the right dir cd $USE_DIR # Make sure log directory exists mkdir -p $USE_DIR/log RUNNER_SCRIPT_DATA= if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/runner_script.data" ]; then RUNNER_SCRIPT_DATA=`cat $RUNNER_BASE_DIR/releases/$APP_VSN/runner_script.data` fi if [ -z "$RUNNER_SCRIPT_DATA" ]; then ROOTDIR=$RUNNER_BASE_DIR ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/nodetool" ]; then NODETOOL="$ERTS_PATH/escript $RUNNER_BASE_DIR/releases/$APP_VSN/nodetool $NAME_ARG $COOKIE_ARG" else NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" fi SLIM_ARGS= elif [ "$RUNNER_SCRIPT_DATA" = "slim" ]; then # Setup system paths SYSTEM_ERL_PATH=`which erl` if [ ! -x "$SYSTEM_ERL_PATH" ]; then echo "Failed to find erl. Is Erlang/OTP available in PATH?" exit 1 fi SYSTEM_HOME_BIN=${SYSTEM_ERL_PATH%/*} ROOTDIR=$SYSTEM_HOME_BIN/../lib/erlang ERTS_PATH=$ROOTDIR/erts-$ERTS_VSN/bin unset SYSTEM_ERL_PATH unset SYSTEM_HOME_BIN LOCAL_ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin NODETOOL="$ERTS_PATH/escript $RUNNER_BASE_DIR/releases/$APP_VSN/nodetool $NAME_ARG $COOKIE_ARG" unset LOCAL_ERL_PATH # Setup additional arguments for slim release SLIM_ARGS="-boot_var RELTOOL_EXT_LIB $RUNNER_BASE_DIR/lib -sasl releases_dir \"$RUNNER_BASE_DIR/releases\"" else echo "Unknown runner_script.data" exit 1 fi # Setup remote shell command to control node REMSH="$ERTS_PATH/erl -hidden $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG" # Common functions # Ping node without allowing nodetool to take stdin ping_node() { $NODETOOL ping < /dev/null } # Set the PID global variable, return 1 on error get_pid() { PID=`$NODETOOL getpid < /dev/null` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" return 1 fi # don't allow empty or init pid's if [ -z $PID ] || [ "$PID" -le 1 ]; then return 1 fi return 0 } # Check the first argument for instructions case "$1" in start|start_boot) # Make sure there is not already a node running RES=`ping_node` if [ "$RES" = "pong" ]; then echo "Node is already running!" exit 1 fi case "$1" in start) shift START_OPTION="console" HEART_OPTION="start" ;; start_boot) shift START_OPTION="console_boot" HEART_OPTION="start_boot" ;; esac RUN_PARAM=$(printf "\'%s\' " "$@") HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT $HEART_OPTION $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 $START_OPTION $RUN_PARAM" 2>&1 ;; stop) # Wait for the node to completely stop... case `uname -s` in Darwin) # Make sure we explicitly set this because iTerm.app doesn't for # some reason. COMMAND_MODE=unix2003 esac # Get the PID from nodetool get_pid GPR=$? if [ "$GPR" -ne 0 ] || [ -z $PID ]; then exit $GPR fi # Tell nodetool to initiate a stop $NODETOOL stop ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi # Wait for the node to completely stop... while `kill -s 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 ping_node ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; attach) # Make sure a node is running ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift exec $ERTS_PATH/to_erl $PIPE_DIR ;; eval) # Make sure a node IS running ping_node > /dev/null 2>&1 ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift $NODETOOL eval "$1" ES_EVAL=$? if [ "$ES_EVAL" -ne 0 ]; then exit $ES_EVAL fi ;; remote_console) # Make sure a node is running ping_node 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 ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi $NODETOOL upgrade $2 ;; console|console_clean|console_boot) # .boot file typically just $SCRIPT (ie, the app name) # however, for debugging, sometimes start_clean.boot is useful. # For e.g. 'setup', one may even want to name another boot script. case "$1" in console) BOOTFILE=$SCRIPT ;; console_clean) BOOTFILE=start_clean ;; console_boot) shift BOOTFILE="$1" shift ;; esac # Setup beam-required vars BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\\///'` CMD="$BINDIR/erlexec $SLIM_ARGS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR 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 BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin EMU=beam PROGNAME=`echo $0 | sed 's/.*\///'` CMD="$BINDIR/erlexec $SLIM_ARGS $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH" export EMU export ROOTDIR export BINDIR export PROGNAME # Dump environment info for logging purposes echo "Exec: $CMD" -- ${1+"$@"} echo "Root: $ROOTDIR" # Start the VM exec $CMD -- ${1+"$@"} ;; getpid) # Get the PID from nodetool get_pid ES=$? if [ "$ES" -ne 0 ] || [ -z $PID ]; then exit $ES fi echo $PID ;; *) echo "Usage: $SCRIPT {start|start_boot |foreground|stop|restart|reboot|ping|console|getpid|console_clean|console_boot |attach|eval|remote_console|upgrade}" exit 1 ;; esac exit 0 rebar-2.6.0/priv/templates/simplenode.sys.config000066400000000000000000000005151254103774400217430ustar00rootroot00000000000000[ %% 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.6.0/priv/templates/simplenode.template000066400000000000000000000012201254103774400214660ustar00rootroot00000000000000{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.6.0/priv/templates/simplenode.vm.args000066400000000000000000000006611254103774400212400ustar00rootroot00000000000000## 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.6.0/priv/templates/simplenode.windows.runner.cmd000066400000000000000000000057401254103774400234320ustar00rootroot00000000000000@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 @rem CWD to the node root directory @cd %node_root% @set releases_dir=%node_root%\releases @rem Parse ERTS version and release version from start_erl.data @for /F "usebackq tokens=1,2" %%I in ("%releases_dir%\start_erl.data") do @( @call :set_trim erts_version %%I @call :set_trim release_version %%J ) @set vm_args=%releases_dir%\%release_version%\vm.args @set sys_config=%releases_dir%\%release_version%\sys.config @set node_boot_script=%releases_dir%\%release_version%\%node_name% @set clean_boot_script=%releases_dir%\%release_version%\start_clean @rem extract erlang cookie from 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% @set erlsrv="%erts_bin%\erlsrv.exe" @set epmd="%erts_bin%\epmd.exe" @set escript="%erts_bin%\escript.exe" @set werl="%erts_bin%\werl.exe" @set nodetool="%erts_bin%\nodetool" @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"=="ping" @goto ping @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^|ping^|query^|attach^|upgrade] @goto :EOF :install @set description=Erlang node %node_name% in %node_root% @set start_erl=%node_root%\bin\start_erl.cmd @set args= ++ %node_name% ++ %node_root% @%erlsrv% add %service_name% -c "%description%" -sname %node_name% -w "%node_root%" -m "%start_erl%" -args "%args%" -stopaction "init:stop()." @goto :EOF :uninstall @%erlsrv% remove %service_name% @%epmd% -kill @goto :EOF :start @%erlsrv% start %service_name% @goto :EOF :stop @%erlsrv% stop %service_name% @goto :EOF :console @start "%node_name% console" %werl% -boot "%node_boot_script%" -config "%sys_config%" -args_file "%vm_args%" -sname %node_name% @goto :EOF :ping @%escript% %nodetool% ping -sname "%node_name%" -setcookie "%erlang_cookie%" @exit %ERRORLEVEL% @goto :EOF :query @%erlsrv% list %service_name% @exit %ERRORLEVEL% @goto :EOF :attach @for /f "usebackq" %%I in (`hostname`) do @set hostname=%%I start "%node_name% attach" %werl% -boot "%clean_boot_script%" -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 ) @%escript% %nodetool% -sname "%node_name%" -setcookie "%erlang_cookie%" upgrade %2 @goto :EOF :set_trim @set %1=%2 @goto :EOF rebar-2.6.0/priv/templates/simplenode.windows.start_erl.cmd000066400000000000000000000023541254103774400241160ustar00rootroot00000000000000@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 @rem Trim spaces from the left of %%K (node_root), which may have spaces inside @for /f "tokens=* delims= " %%a in ("%%K") do @set node_root=%%a ) @set releases_dir=%node_root%\releases @rem parse ERTS version and release version from start_erl.dat @for /F "usebackq 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.6.0/priv/templates/simplesrv.erl000066400000000000000000000026241254103774400203330ustar00rootroot00000000000000-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.6.0/priv/templates/simplesrv.template000066400000000000000000000001261254103774400213570ustar00rootroot00000000000000{variables, [{srvid, "myserver"}]}. {template, "simplesrv.erl", "src/{{srvid}}.erl"}. rebar-2.6.0/rebar.config000066400000000000000000000026071254103774400151100ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et %% escript_incl_extra is for internal rebar-private use only. %% Do not use outside rebar. Config interface is not stable. {escript_incl_extra, [{"priv/templates/*", "."}]}. %% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17. %% At the same time, their counterparts dict() and digraph() are to be %% deprecated in Erlang 18. namespaced_types option is used to select proper %% type name depending on the OTP version used. {erl_opts, [ {platform_define, "^[0-9]+", namespaced_types} ]}. {xref_checks, []}. {xref_queries, [{"(XC - UC) || (XU - X - B - (\"escript\":\"foldl\"/\"3\") - (\"eunit_test\":\"function_wrapper\"/\"2\") - (\"eflame\":\"apply\"/\"5\") - (\"abnfc\":\"file\"/\"2\") - (\"erlydtl\":\"compile\"/\"3\") - (\"lfe_comp\":\"file\"/\"2\") - (\"neotoma\":\"file\"/\"2\") - (\"protobuffs_compile\":\"scan_file\"/\"2\") - (\"gpb_compile\":\"file\"/\"2\") - (\"gpb_compile\":\"format_error\"/\"1\") - (\"diameter_codegen\":\"from_dict\"/\"4\") - (\"diameter_dict_util\":\"format_error\"/\"1\") - (\"diameter_dict_util\":\"parse\"/\"2\"))", []}]}. {dialyzer, [ {plt_extra_apps, [diameter]}, {warnings, [ unmatched_returns, error_handling, race_conditions ]} ]}. rebar-2.6.0/rebar.config.sample000066400000000000000000000206621254103774400163710ustar00rootroot00000000000000%% -*- 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 == %% Extend list of always recursive commands {recursive_cmds, []}. %% Check required ERTS or OTP release version {require_erts_vsn, ".*"}. {require_otp_vsn, ".*"}. {require_min_otp_vsn, ".*"}. %% 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, ["src/mymib1.erl", "src/mymib2.erl"]}. %% Erlang compiler options {erl_opts, [no_debug_info, {i, "myinclude"}, {src_dirs, ["src", "src2", "src3"]}, {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, []}. %% leex options {xrl_opts, []}. %% leex files to compile first {xrl_first_files, []}. %% yecc options {yrl_opts, []}. %% yecc files to compile first {yrl_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, []}]}]}. %% == escriptize == {escript_name, "application"}. {escript_incl_apps, []}. {escript_shebang, "#!/usr/bin/env escript\n"}. {escript_comment, "%%\n"}. {escript_emu_args, "%%! -pa application/application/ebin\n"}. %% == 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, []}. %% == Proto compiler == {proto_opts, [ {compiler, protobuffs}, {src_dirs, ["src"]} ]}. %% Available compilers for protocol buffer files (*.proto): %% protobuffs (default) %% gpb %% Optional src_dirs which is a list of directories where %% to look for .proto files, default is src %% Options for the gpb protocol buffer compiler, %% if selected by the proto_compiler option {gpb_opts, []}. %% == Diameter compiler == %% Diameter files to compile before the rest {dia_first_files, []}. %% Options for the diameter compiler {dia_opts, []}. %% == EUnit == %% Options for eunit:test() {eunit_opts, []}. %% Additional compile options for eunit. erl_opts is also used {eunit_compile_opts, []}. %% Same as erl_first_files, but used only when running 'eunit' {eunit_first_files, []}. %% == Cover == %% 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}. %% Whether to export coverage report to file. Default is `false' {cover_export_enabled, false}. %% == Common Test == %% Override the default "test" directory in which SUITEs are located {ct_dir, "itest"}. %% Override the default "logs" directory in which SUITEs are logged {ct_log_dir, "test/logs"}. %% Option to pass extra parameters when launching Common Test {ct_extra_params, "-boot start_sasl -s myapp"}. %% Option to use short names (i.e., -sname test) when starting ct {ct_use_short_names, true}. %% == QuickCheck == %% If qc_mod is unspecified, rebar tries to detect Triq or EQC {qc_opts, [{qc_mod, module()}, Options]}. %% Additional compile options for qc. erl_opts is also used {qc_compile_opts, []}. %% Same as erl_first_files, but used only when running 'qc' {qc_first_files, []}. %% == Cleanup == %% Which files to cleanup {clean_files, ["file", "file2"]}. %% == OTP Applications == %% 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, svn, rsync, fossil, and p4. {deps, [app_name, {rebar, "1.0.*"}, {rebar, ".*", {git, "git://github.com/rebar/rebar.git"}}, {rebar, ".*", {git, "git://github.com/rebar/rebar.git", "Rev"}}, {rebar, "1.0.*", {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, {rebar, "1.0.0", {git, "git://github.com/rebar/rebar.git", {tag, "1.0.0"}}}, %% Dependencies can be marked as 'raw'. Rebar does not require %% such dependencies to have a standard Erlang/OTP layout %% which assumes the presence of either %% "src/dependency_name.app.src" or "ebin/dependency_name.app" %% files. %% %% 'raw' dependencies can still contain 'rebar.config' and %% even can have the proper OTP directory layout, but they %% won't be compiled. %% %% Only a subset of rebar commands will be executed on the %% 'raw' subdirectories: get-deps, update-deps, check-deps, %% list-deps and delete-deps. {rebar, "", {git, "git://github.com/rebar/rebar.git", {branch, "master"}}, [raw]}, {app_name, ".*", {hg, "https://www.example.org/url"}}, {app_name, ".*", {rsync, "Url"}}, {app_name, ".*", {svn, "https://www.example.org/url"}}, {app_name, ".*", {svn, "svn://svn.example.org/url"}}, {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}}, {app_name, ".*", {fossil, "https://www.example.org/url"}}, {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}, {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]}. %% == 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}. %% optional extra paths to include in xref:set_library_path/2. %% specified relative location of rebar.config. %% e.g. {xref_extra_paths,["../gtknode/src"]} {xref_extra_paths,[]}. %% xref checks to run {xref_checks, [undefined_function_calls, undefined_functions, locals_not_used, exports_not_used, deprecated_function_calls, deprecated_functions]}. %% 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 mod:*foo/4 %% functions from undefined external function calls as those are in a %% generated module {xref_queries, [{"(XC - UC) || (XU - X - B" " - (\"mod\":\".*foo\"/\"4\"))",[]}]}. %% == Dialyzer == {dialyzer, [ %% Store PLT locally inside the project in .rebar (Default) {plt_location, local}, %% Store PLT in custom directory {plt_location, "custom_dir"}, %% Extra apps to include in the PLT {plt_extra_apps, [app1, app2]}, {warnings, [unmatched_returns, error_handling]} ]}. rebar-2.6.0/rebar.config.script000066400000000000000000000010231254103774400164020ustar00rootroot00000000000000%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et ExtraDeps = [{retest, ".*", {git, "git://github.com/dizzyd/retest.git"}}], case os:getenv("REBAR_EXTRA_DEPS") of false -> CONFIG; _ -> case lists:keysearch(deps, 1, CONFIG) of {value, {deps, Deps}} -> NDeps = Deps ++ ExtraDeps, lists:keyreplace(deps, 1, CONFIG, {deps, NDeps}); false -> CONFIG ++ [{deps, ExtraDeps}] end end. rebar-2.6.0/src/000077500000000000000000000000001254103774400134105ustar00rootroot00000000000000rebar-2.6.0/src/rebar.erl000066400000000000000000000557261254103774400152260ustar00rootroot00000000000000%% -*- 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, run/2, help/0, parse_args/1, version/0, get_jobs/1]). -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. -define(DEFAULT_JOBS, 3). %% ==================================================================== %% Public API %% ==================================================================== %% escript Entry point main(Args) -> case catch(run(Args)) of ok -> ok; rebar_abort -> rebar_utils:delayed_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]), rebar_utils:delayed_halt(1) end. %% Erlang-API entry point run(BaseConfig, Commands) -> _ = application:load(rebar), run_aux(BaseConfig, Commands). %% ==================================================================== %% Internal functions %% ==================================================================== run(["help"|RawCmds]) when RawCmds =/= [] -> ok = load_rebar_app(), Cmds = unabbreviate_command_names(RawCmds), Args = parse_args(Cmds), BaseConfig = init_config(Args), {BaseConfig1, _} = save_options(BaseConfig, Args), BaseConfig2 = init_config1(BaseConfig1), rebar_core:help(BaseConfig2, [list_to_atom(C) || C <- Cmds]); run(["help"]) -> help(); run(["info"|_]) -> %% Catch calls to 'rebar info' to avoid treating plugins' info/2 functions %% as commands. ?CONSOLE("Command 'info' not understood or not applicable~n", []); run(["version"]) -> ok = load_rebar_app(), %% Display vsn and build time info version(); run(RawArgs) -> ok = load_rebar_app(), %% Parse out command line arguments -- what's left is a list of commands to %% run -- and start running commands Args = parse_args(RawArgs), BaseConfig = init_config(Args), {BaseConfig1, Cmds} = save_options(BaseConfig, Args), case rebar_config:get_xconf(BaseConfig1, enable_profiling, false) of true -> ?CONSOLE("Profiling!\n", []), profile(BaseConfig1, Cmds); false -> run_aux(BaseConfig1, Cmds) end. load_rebar_app() -> %% Pre-load the rebar app so that we get default configuration ok = application:load(rebar). init_config({Options, _NonOptArgs}) -> %% 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, %% Set the rebar config to use GlobalConfig1 = case proplists:get_value(config, Options) of undefined -> GlobalConfig; Conf -> rebar_config:set_global(GlobalConfig, config, Conf) end, GlobalConfig2 = set_log_level(GlobalConfig1, Options), %% Initialize logging system ok = rebar_log:init(GlobalConfig2), BaseConfig = rebar_config:base_config(GlobalConfig2), %% Keep track of how many operations we do, so we can detect bad commands BaseConfig1 = rebar_config:set_xconf(BaseConfig, operations, 0), %% Initialize vsn cache rebar_config:set_xconf(BaseConfig1, vsn_cache, dict:new()). init_config1(BaseConfig) -> %% Determine the location of the rebar executable; important for pulling %% resources out of the escript ScriptName = filename:absname(escript:script_name()), BaseConfig1 = rebar_config:set_xconf(BaseConfig, escript, ScriptName), ?DEBUG("Rebar location: ~p\n", [ScriptName]), %% Note the top-level directory for reference AbsCwd = filename:absname(rebar_utils:get_cwd()), rebar_config:set_xconf(BaseConfig1, base_dir, AbsCwd). profile(BaseConfig1, Commands) -> ?CONSOLE("Please take note that profiler=[fprof|eflame] is preliminary" " and will be~nreplaced with a different command line flag" " in the next release.~n", []), Profiler = rebar_config:get_global(BaseConfig1, profiler, "fprof"), profile(BaseConfig1, Commands, list_to_atom(Profiler)). profile(Config, Commands, fprof) -> try fprof:apply(fun run_aux/2, [Config, Commands]) after ok = fprof:profile(), ok = fprof:analyse([{dest, "fprof.analysis"}]), case rebar_utils:find_executable("erlgrind") of false -> ?CONSOLE( "See fprof.analysis (generated from fprof.trace)~n", []), ok; ErlGrind -> Cmd = ?FMT("~s fprof.analysis fprof.cgrind", [ErlGrind]), {ok, []} = rebar_utils:sh(Cmd, [{use_stdout, false}, abort_on_error]), ?CONSOLE("See fprof.analysis (generated from fprof.trace)" " and fprof.cgrind~n", []), ok end end; profile(Config, Commands, eflame) -> case code:lib_dir(eflame) of {error, bad_name} -> ?ABORT("eflame not found in code path~n", []), ok; EflameDir -> Trace = "eflame.trace", try eflame:apply(normal_with_children, Trace, rebar, run, [Config, Commands]) after %% generate flame graph Script = filename:join(EflameDir, "stack_to_flame.sh"), Svg = "eflame.svg", %% stack_to_flame.sh < eflame.trace > eflame.png Cmd = ?FMT("~s < ~s > ~s", [Script, Trace, Svg]), {ok, []} = rebar_utils:sh(Cmd, [{use_stdout, false}, abort_on_error]), ?CONSOLE("See eflame.svg (generated from eflame.trace)~n", []), ok end end; profile(_Config, _Commands, Profiler) -> ?ABORT("Unsupported profiler: ~s~n", [Profiler]). run_aux(BaseConfig, Commands) -> %% Make sure crypto is running case crypto:start() of ok -> ok; {error,{already_started,crypto}} -> ok end, %% Make sure memoization server is running case rmemo:start() of {ok, _} -> ok; {error, {already_started, _}} -> ok end, %% Convert command strings to atoms CommandAtoms = [list_to_atom(C) || C <- Commands], BaseConfig1 = init_config1(BaseConfig), %% Process each command, resetting any state between each one rebar_core:process_commands(CommandAtoms, BaseConfig1). %% %% print help/usage string %% help() -> OptSpecList = option_spec_list(), rebar_getopt:usage(OptSpecList, "rebar", "[var=value,...] ", [{"var=value", "rebar global variables (e.g. force=1)"}, {"command", "Command to run (e.g. compile)"}]), ?CONSOLE("To see a list of built-in commands, execute rebar -c.~n~n", []), ?CONSOLE( "Type 'rebar help ' for help on specific commands." "~n~n", []), ?CONSOLE( "rebar allows you to abbreviate the command to run:~n" "$ rebar co # same as rebar compile~n" "$ rebar eu # same as rebar eunit~n" "$ rebar g-d # same as rebar get-deps~n" "$ rebar x eu # same as rebar xref eunit~n" "$ rebar l-d # same as rebar list-deps~n" "$ rebar l-d l-t # same as rebar list-deps list-templates~n" "$ rebar list-d l-te # same as rebar list-deps list-templates~n" "~n", []), ?CONSOLE( "Core rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" "Core command line options:~n" " apps=app1,app2 (specify apps to process)~n" " skip_apps=app1,app2 (specify apps to skip)~n", [ {recursive_cmds, []}, {require_erts_vsn, ".*"}, {require_otp_vsn, ".*"}, {require_min_otp_vsn, ".*"}, {lib_dirs, []}, {sub_dirs, ["dir1", "dir2"]}, {plugins, [plugin1, plugin2]}, {plugin_dir, "some_other_directory"}, {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"}]} ]). %% %% 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(RawArgs) -> %% Parse getopt options OptSpecList = option_spec_list(), case rebar_getopt:parse(OptSpecList, RawArgs) of {ok, Args} -> Args; {error, {Reason, Data}} -> ?ERROR("~s ~p~n~n", [Reason, Data]), help(), rebar_utils:delayed_halt(1) end. save_options(Config, {Options, NonOptArgs}) -> %% Check options and maybe halt execution ok = show_info_maybe_halt(Options, NonOptArgs), GlobalDefines = proplists:get_all_values(defines, Options), Config1 = rebar_config:set_xconf(Config, defines, GlobalDefines), %% Setup profiling flag Config2 = rebar_config:set_xconf(Config1, enable_profiling, proplists:get_bool(profile, Options)), %% Setup flag to keep running after a single command fails Config3 = rebar_config:set_xconf(Config2, keep_going, proplists:get_bool(keep_going, Options)), %% Setup flag to enable recursive application of commands Config4 = rebar_config:set_xconf(Config3, recursive, proplists:get_bool(recursive, Options)), %% Set global variables based on getopt options Config5 = set_global_flag(Config4, Options, force), Config6 = case proplists:get_value(jobs, Options, ?DEFAULT_JOBS) of ?DEFAULT_JOBS -> Config5; Jobs -> rebar_config:set_global(Config5, jobs, Jobs) 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. {Config7, RawCmds} = filter_flags(Config6, NonOptArgs, []), {Config7, unabbreviate_command_names(RawCmds)}. %% %% set log level based on getopt option %% set_log_level(Config, Options) -> {IsVerbose, Level} = case proplists:get_bool(quiet, Options) of true -> {false, rebar_log:error_level()}; false -> DefaultLevel = rebar_log:default_level(), case proplists:get_all_values(verbose, Options) of [] -> {false, DefaultLevel}; Verbosities -> {true, DefaultLevel + lists:last(Verbosities)} end end, case IsVerbose of true -> Config1 = rebar_config:set_xconf(Config, is_verbose, true), rebar_config:set_global(Config1, verbose, Level); false -> rebar_config:set_global(Config, verbose, Level) end. %% %% 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(Config, Options, Flag) -> Value = case proplists:get_bool(Flag, Options) of true -> "1"; false -> "0" end, rebar_config:set_global(Config, 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(), rebar_utils:delayed_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 escriptize Generate escript archive create template= [var=foo,...] Create skel based on template and vars create-app [appid=myapp] Create simple app skel create-lib [libid=mylib] Create simple lib skel create-node [nodeid=mynode] Create simple node skel list-templates List available templates doc Generate Erlang program documentation prepare-deps Run 'rebar -r get-deps compile' refresh-deps Run 'rebar -r update-deps compile' 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[s]=foo] Run EUnit tests in foo.erl and test/foo_tests.erl [suite[s]=foo] [test[s]=bar] Run specific EUnit tests [first test name starting with 'bar' in foo.erl and test/foo_tests.erl] [test[s]=bar] For every existing suite, run the first test whose name starts with bar and, if no such test exists, run the test whose name starts with bar in the suite's _tests module. [random_suite_order=true] Run tests in a random order, either [random_suite_order=Seed] with a random seed for the PRNG, or a specific one. ct [suite[s]= [group[s]= [case[s]=]]] Run common_test suites qc Test QuickCheck properties xref Run cross reference analysis dialyze Analyze the code for discrepancies build-plt Build project-specific PLT check-plt Check the PLT for consistency and rebuild it if it is not up-to-date delete-plt Delete project-specific PLT shell Start a shell similar to 'erl -pa ebin -pa deps/*/ebin' help Show the program options version Show version information ">>, io:put_chars(S). get_jobs(Config) -> rebar_config:get_global(Config, jobs, ?DEFAULT_JOBS). %% %% options accepted via getopt %% option_spec_list() -> Jobs = ?DEFAULT_JOBS, JobsHelp = io_lib:format( "Number of concurrent workers a command may use. Default: ~B", [Jobs]), [ %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} {help, $h, "help", undefined, "Show the program options"}, {commands, $c, "commands", undefined, "Show available commands"}, {verbose, $v, "verbose", integer, "Verbosity level (-v, -vv)"}, {quiet, $q, "quiet", boolean, "Quiet, only print error messages"}, {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. Via profiler= you can optionally select " "either fprof (default) or eflame. The result can be found in " "fprof.analysis or eflame.svg."}, {keep_going, $k, "keep-going", undefined, "Keep running after a command fails"}, {recursive, $r, "recursive", boolean, "Apply all commands recursively. Alternatively, you can selectively" " configure what other commands in addition to the always-recursive" " ones (compile, *-deps) should also be applied recursively." " For example, to make 'eunit' recursive, add {recursive_cmds, [eunit]}" " to rebar.config."} ]. %% %% Seperate all commands (single-words) from flags (key=value) and store %% values into the rebar_config global storage. %% filter_flags(Config, [], Commands) -> {Config, lists:reverse(Commands)}; filter_flags(Config, [Item | Rest], Commands) -> case string:tokens(Item, "=") of [Command] -> filter_flags(Config, Rest, [Command | Commands]); [KeyStr, RawValue] -> Key = list_to_atom(KeyStr), Value = case Key of verbose -> list_to_integer(RawValue); _ -> RawValue end, Config1 = rebar_config:set_global(Config, Key, Value), filter_flags(Config1, Rest, Commands); Other -> ?CONSOLE("Ignoring command line argument: ~p\n", [Other]), filter_flags(Config, Rest, Commands) end. command_names() -> [ "build-plt", "check-deps", "check-plt", "clean", "compile", "create", "create-app", "create-lib", "create-node", "ct", "delete-plt", "delete-deps", "dialyze", "doc", "eunit", "escriptize", "generate", "generate-appups", "generate-upgrade", "get-deps", "help", "list-deps", "list-templates", "prepare-deps", "qc", "refresh-deps", "update-deps", "overlay", "shell", "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.6.0/src/rebar_abnfc_compiler.erl000066400000000000000000000110251254103774400202310ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/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 %% =================================================================== info(help, compile) -> ?CONSOLE( "Build ABNF (*.abnf) sources.~n" "~n" "Valid rebar.config options:~n" " ~p~n", [ {abnfc_opts, [{doc_root, "src"}, {out_dir, "src"}, {source_ext, ".abnfc"}, {module_ext, ""}]} ]). 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.6.0/src/rebar_app_utils.erl000066400000000000000000000170171254103774400172750ustar00rootroot00000000000000%% -*- 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/2, app_applications/2, app_vsn/2, is_skipped_app/2]). -export([load_app_file/2]). % 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"]), AppSrcScript = filename:join([SrcDir, "*.app.src.script"]), AppSrc = filename:join([SrcDir, "*.app.src"]), case {filelib:wildcard(AppSrcScript), filelib:wildcard(AppSrc)} of {[AppSrcScriptFile], _} -> {true, AppSrcScriptFile}; {[], [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") orelse Filename =/= filename:rootname(Filename, ".app.src.script"). app_src_to_app(Filename) -> Filebase = case filename:rootname(Filename, ".app.src") of Filename -> filename:basename(Filename, ".app.src.script"); _ -> filename:basename(Filename, ".app.src") end, filename:join("ebin", Filebase ++ ".app"). app_name(Config, AppFile) -> case load_app_file(Config, AppFile) of {ok, NewConfig, AppName, _} -> {NewConfig, AppName}; {error, Reason} -> ?ABORT("Failed to extract name from ~s: ~p\n", [AppFile, Reason]) end. app_applications(Config, AppFile) -> case load_app_file(Config, AppFile) of {ok, NewConfig, _, AppInfo} -> {NewConfig, get_value(applications, AppInfo, AppFile)}; {error, Reason} -> ?ABORT("Failed to extract applications from ~s: ~p\n", [AppFile, Reason]) end. app_vsn(Config, AppFile) -> case load_app_file(Config, AppFile) of {ok, Config1, _, AppInfo} -> AppDir = filename:dirname(filename:dirname(AppFile)), rebar_utils:vcs_vsn(Config1, get_value(vsn, AppInfo, AppFile), AppDir); {error, Reason} -> ?ABORT("Failed to extract vsn from ~s: ~p\n", [AppFile, Reason]) end. is_skipped_app(Config, AppFile) -> {Config1, ThisApp} = app_name(Config, AppFile), %% Check for apps global parameter; this is a comma-delimited list %% of apps on which we want to run commands Skipped = case get_apps(Config) of undefined -> %% No apps parameter specified, check the skip_apps list.. case get_skip_apps(Config) of undefined -> %% No skip_apps list, run everything.. false; SkipApps -> TargetApps = [list_to_atom(A) || A <- string:tokens(SkipApps, ",")], is_skipped(ThisApp, TargetApps) end; Apps -> %% run only selected apps TargetApps = [list_to_atom(A) || A <- string:tokens(Apps, ",")], is_selected(ThisApp, TargetApps) end, {Config1, Skipped}. %% =================================================================== %% Internal functions %% =================================================================== load_app_file(Config, Filename) -> AppFile = {app_file, Filename}, case rebar_config:get_xconf(Config, {appfile, AppFile}, undefined) of undefined -> case consult_app_file(Filename) of {ok, {application, AppName, AppData}} -> Config1 = rebar_config:set_xconf(Config, {appfile, AppFile}, {AppName, AppData}), {ok, Config1, AppName, AppData}; {error, _} = Error -> {error, {error, Error}}; Other -> {error, {unexpected_terms, Other}} end; {AppName, AppData} -> {ok, Config, AppName, AppData} end. %% In the case of *.app.src we want to give the user the ability to %% dynamically script the application resource file (think dynamic version %% string, etc.), in a way similar to what can be done with the rebar %% config. However, in the case of *.app, rebar should not manipulate %% that file. This enforces that dichotomy between app and app.src. consult_app_file(Filename) -> Result = case lists:suffix(".app", Filename) of true -> file:consult(Filename); false -> rebar_config:consult_file(Filename) end, case Result of {ok, [Term]} -> {ok, Term}; _ -> Result 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(ThisApp, TargetApps) -> case lists:member(ThisApp, TargetApps) of false -> {true, ThisApp}; true -> false end. %% skip_apps= for filtering apps is_skipped(ThisApp, TargetApps) -> case lists:member(ThisApp, TargetApps) of false -> false; true -> {true, ThisApp} end. get_apps(Config) -> rebar_config:get_global(Config, apps, undefined). get_skip_apps(Config) -> rebar_config:get_global(Config, skip_apps, undefined). rebar-2.6.0/src/rebar_appups.erl000066400000000000000000000210221254103774400165740ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/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 {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile), TargetParentDir = rebar_rel_utils:get_target_parent_dir(Config, ReltoolConfig), PrevRelPath = rebar_rel_utils:get_previous_release_path(Config), OldVerPath = filename:join([TargetParentDir, PrevRelPath]), ModDeps = rebar_config:get(Config, module_deps, []), %% Get the new and old release name and versions {Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig), NewVerPath = rebar_rel_utils:get_target_dir(Config, ReltoolConfig), {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_by_ext( 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, ModDeps, UpgradeApps), {ok, Config1}. %% =================================================================== %% Internal functions %% =================================================================== info(help, 'generate-appups') -> ?CONSOLE("Generate appup files.~n" "~n" "Valid command line options:~n" " previous_release=path~n", []). 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, ModDeps, [{_App, {undefined, _}}|Rest]) -> generate_appup_files(NewVerPath, OldVerPath, ModDeps, Rest); generate_appup_files(NewVerPath, OldVerPath, ModDeps, [{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), ChangedNames = [list_to_atom(file_to_name(F)) || {F, _} <- ChangedFiles], ModDeps1 = [{N, [M1 || M1 <- M, lists:member(M1, ChangedNames)]} || {N, M} <- ModDeps], Added = [generate_instruction(added, File) || File <- AddedFiles], Deleted = [generate_instruction(deleted, File) || File <- DeletedFiles], Changed = [generate_instruction(changed, ModDeps1, 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, ModDeps, 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, ModDeps, {File, _}) -> {ok, {Name, List}} = beam_lib:chunks(File, [attributes, exports]), Behavior = get_behavior(List), CodeChange = is_code_change(List), Deps = proplists:get_value(Name, ModDeps, []), generate_instruction_advanced(Name, Behavior, CodeChange, Deps). generate_instruction_advanced(Name, undefined, undefined, Deps) -> %% Not a behavior or code change, assume purely functional {load_module, Name, Deps}; generate_instruction_advanced(Name, [supervisor], _, _) -> %% Supervisor {update, Name, supervisor}; generate_instruction_advanced(Name, _, code_change, Deps) -> %% Includes code_change export {update, Name, {advanced, []}, Deps}; generate_instruction_advanced(Name, _, _, Deps) -> %% Anything else {load_module, Name, Deps}. 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.6.0/src/rebar_asn1_compiler.erl000066400000000000000000000074071254103774400200330ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== -spec compile(rebar_config:config(), 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(rebar_config:config(), file:filename()) -> 'ok'. clean(_Config, _AppFile) -> GeneratedFiles = asn_generated_files("asn1", "src", "include"), ok = rebar_file_utils:delete_each(GeneratedFiles), ok. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> info_help("Build ASN.1 (*.asn1) sources"); info(help, clean) -> info_help("Delete ASN.1 (*.asn1) results"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " {asn1_opts, []} (see asn1ct:compile/2 documentation)~n", [Description]). -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.6.0/src/rebar_base_compiler.erl000066400000000000000000000246471254103774400201100ustar00rootroot00000000000000%% -*- 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, run/5, ok_tuple/3, error_tuple/5]). %% =================================================================== %% 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:get_jobs(Config), ?DEBUG("Starting ~B compile worker(s)~n", [Jobs]), Pids = [spawn_monitor(F) || _I <- lists:seq(1,Jobs)], compile_queue(Config, 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) -> %% Find all possible source files Recursive = proplists:get_value(recursive, Opts, true), FoundFiles = rebar_utils:find_files_by_ext(SourceDir, SourceExt, Recursive), %% Remove first files from found files RestFiles = [Source || Source <- FoundFiles, not lists:member(Source, FirstFiles)], FirstUnits = source_to_unit_each(FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt), RestUnits = source_to_unit_each(RestFiles, SourceDir, SourceExt, TargetDir, TargetExt), run(Config, FirstUnits, RestUnits, Compile3Fn, Opts). %% FirstUnits and RestUnits are lists of tuples: {Source,Target} run(Config, FirstUnits, RestUnits, Compile3Fn, Opts) -> %% Check opts for flag indicating that compile should check lastmod CheckLastMod = proplists:get_bool(check_last_mod, Opts), run(Config, FirstUnits, RestUnits, fun({S, Target}, C) -> simple_compile_wrapper(S, Target, Compile3Fn, C, CheckLastMod) end). ok_tuple(Config, Source, Ws) -> {ok, format_warnings(Config, Source, Ws)}. error_tuple(Config, Source, Es, Ws, Opts) -> {error, format_errors(Config, Source, Es), format_warnings(Config, Source, Ws, Opts)}. %% =================================================================== %% 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. source_to_unit_each(Files, SourceDir, SourceExt, TargetDir, TargetExt) -> [{File, target_file(File, SourceDir, SourceExt, TargetDir, TargetExt)} || File <- Files]. target_file(SourceFile, SourceDir, SourceExt, TargetDir, TargetExt) -> BaseFile = remove_common_path(SourceFile, SourceDir), filename:join([TargetDir, 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_each([], _Config, _CompileFn) -> ok; compile_each([Unit | Rest], Config, CompileFn) -> case CompileFn(Unit, Config) of ok -> ?CONSOLE("Compiled ~s\n", [unit_source(Unit)]); {ok, Warnings} -> report(Warnings), ?CONSOLE("Compiled ~s\n", [unit_source(Unit)]); skipped -> ?INFO("Skipped ~s\n", [unit_source(Unit)]); Error -> maybe_report(Error), ?CONSOLE("Compiling ~s failed:\n", [maybe_absname(Config, unit_source(Unit))]), ?DEBUG("Compilation failed: ~p\n", [Error]), case rebar_config:get_xconf(Config, keep_going, false) of false -> ?FAIL; true -> ?WARN("Continuing after build error\n", []) end end, compile_each(Rest, Config, CompileFn). unit_source({Source, _Target}) -> Source; unit_source(Source) -> Source. compile_queue(_Config, [], []) -> ok; compile_queue(Config, Pids, Targets) -> receive {next, Worker} -> case Targets of [] -> Worker ! empty, compile_queue(Config, Pids, Targets); [Source | Rest] -> Worker ! {compile, Source}, compile_queue(Config, Pids, Rest) end; {fail, {_, {source, Unit}}=Error} -> maybe_report(Error), ?CONSOLE("Compiling ~s failed:\n", [maybe_absname(Config, unit_source(Unit))]), ?DEBUG("Worker compilation failed: ~p\n", [Error]), case rebar_config:get_xconf(Config, keep_going, false) of false -> ?FAIL; true -> ?WARN("Continuing after build error\n", []), compile_queue(Config, Pids, Targets) end; {compiled, Unit, Warnings} -> report(Warnings), ?CONSOLE("Compiled ~s\n", [unit_source(Unit)]), compile_queue(Config, Pids, Targets); {compiled, Unit} -> ?CONSOLE("Compiled ~s\n", [unit_source(Unit)]), compile_queue(Config, Pids, Targets); {skipped, Unit} -> ?INFO("Skipped ~s\n", [unit_source(Unit)]), compile_queue(Config, Pids, Targets); {'DOWN', Mref, _, Pid, normal} -> ?DEBUG("Worker exited cleanly\n", []), Pids2 = lists:delete({Pid, Mref}, Pids), compile_queue(Config, 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(CompileFn(Source, Config)) of {ok, Ws} -> QueuePid ! {compiled, Source, Ws}, compile_worker(QueuePid, Config, CompileFn); 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}}}, case rebar_config:get_xconf(Config, keep_going, false) of false -> ok; true -> compile_worker(QueuePid, Config, CompileFn) end end; empty -> ok end. format_errors(Config, Source, Errors) -> format_errors(Config, Source, "", Errors). format_warnings(Config, Source, Warnings) -> format_warnings(Config, Source, Warnings, []). format_warnings(Config, Source, Warnings, Opts) -> Prefix = case lists:member(warnings_as_errors, Opts) of true -> ""; false -> "Warning: " end, format_errors(Config, Source, Prefix, Warnings). maybe_report({{error, {error, _Es, _Ws}=ErrorsAndWarnings}, {source, _}}) -> maybe_report(ErrorsAndWarnings); maybe_report([{error, E}, {source, S}]) -> report(["unexpected error compiling " ++ S, io_lib:fwrite("~n~p~n", [E])]); maybe_report({error, Es, Ws}) -> report(Es), report(Ws); maybe_report(_) -> ok. report(Messages) -> lists:foreach(fun(Msg) -> io:format("~s", [Msg]) end, Messages). format_errors(Config, _MainSource, Extra, Errors) -> [begin AbsSource = maybe_absname(Config, Source), [format_error(AbsSource, Extra, Desc) || Desc <- Descs] end || {Source, Descs} <- Errors]. format_error(AbsSource, Extra, {{Line, Column}, Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), ?FMT("~s:~w:~w: ~s~s~n", [AbsSource, Line, Column, Extra, ErrorDesc]); format_error(AbsSource, Extra, {Line, Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), ?FMT("~s:~w: ~s~s~n", [AbsSource, Line, Extra, ErrorDesc]); format_error(AbsSource, Extra, {Mod, Desc}) -> ErrorDesc = Mod:format_error(Desc), ?FMT("~s: ~s~s~n", [AbsSource, Extra, ErrorDesc]). maybe_absname(Config, Filename) -> case rebar_utils:processing_base_dir(Config) of true -> Filename; false -> filename:absname(Filename) end. rebar-2.6.0/src/rebar_cleaner.erl000066400000000000000000000043041254103774400167010ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/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). %% =================================================================== %% Internal functions %% =================================================================== info(help, clean) -> ?CONSOLE( "Delete list of files.~n" "~n" "Valid rebar.config options:~n" " ~p~n", [ {clean_files, ["file", "file2"]} ]). rebar-2.6.0/src/rebar_config.erl000066400000000000000000000243561254103774400165460ustar00rootroot00000000000000%% -*- 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/3, get_global/3, is_recursive/1, save_env/3, get_env/2, reset_envs/1, set_skip_dir/2, is_skip_dir/2, reset_skip_dirs/1, clean_config/2, set_xconf/3, get_xconf/2, get_xconf/3, erase_xconf/2]). -include("rebar.hrl"). -ifdef(namespaced_types). %% dict:dict() exists starting from Erlang 17. -type rebar_dict() :: dict:dict(term(), term()). -else. %% dict() has been obsoleted in Erlang 17 and deprecated in 18. -type rebar_dict() :: dict(). -endif. -type key() :: atom(). -record(config, { dir :: file:filename(), opts = [] :: list(), globals = new_globals() :: rebar_dict(), envs = new_env() :: rebar_dict(), %% cross-directory/-command config skip_dirs = new_skip_dirs() :: rebar_dict(), xconf = new_xconf() :: rebar_dict() }). -export_type([config/0]). -opaque config() :: #config{}. -define(DEFAULT_NAME, "rebar.config"). %% =================================================================== %% Public API %% =================================================================== -spec base_config(config()) -> config(). base_config(GlobalConfig) -> ConfName = rebar_config:get_global(GlobalConfig, config, ?DEFAULT_NAME), new(GlobalConfig, ConfName). -spec new() -> config(). new() -> #config{dir = rebar_utils:get_cwd()}. -spec new(file:filename() | config()) -> config(). 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, globals=Globals, skip_dirs=SkipDirs, xconf=Xconf}) -> new(#config{opts=Opts0, globals=Globals, skip_dirs=SkipDirs, xconf=Xconf}, ?DEFAULT_NAME). -spec get(config(), key(), term()) -> term(). get(Config, Key, Default) -> proplists:get_value(Key, Config#config.opts, Default). -spec get_list(config(), key(), term()) -> term(). get_list(Config, Key, Default) -> get(Config, Key, Default). -spec get_local(config(), key(), term()) -> term(). get_local(Config, Key, Default) -> proplists:get_value(Key, local_opts(Config#config.opts, []), Default). -spec get_all(config(), key()) -> list(term()). get_all(Config, Key) -> proplists:get_all_values(Key, Config#config.opts). -spec set(config(), key(), term()) -> config(). set(Config, Key, Value) -> Opts = proplists:delete(Key, Config#config.opts), Config#config { opts = [{Key, Value} | Opts] }. -spec set_global(config(), key(), term()) -> config(). set_global(Config, jobs=Key, Value) when is_list(Value) -> set_global(Config, Key, list_to_integer(Value)); set_global(Config, jobs=Key, Value) when is_integer(Value) -> NewGlobals = dict:store(Key, erlang:max(1, Value), Config#config.globals), Config#config{globals = NewGlobals}; set_global(Config, Key, Value) -> NewGlobals = dict:store(Key, Value, Config#config.globals), Config#config{globals = NewGlobals}. -spec get_global(config(), key(), term()) -> term(). get_global(Config, Key, Default) -> case dict:find(Key, Config#config.globals) of error -> Default; {ok, Value} -> Value end. -spec is_recursive(config()) -> boolean(). is_recursive(Config) -> get_xconf(Config, recursive, false). -spec consult_file(file:filename()) -> term(). 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. -spec save_env(config(), module(), nonempty_list()) -> config(). save_env(Config, Mod, Env) -> NewEnvs = dict:store(Mod, Env, Config#config.envs), Config#config{envs = NewEnvs}. -spec get_env(config(), module()) -> term(). get_env(Config, Mod) -> dict:fetch(Mod, Config#config.envs). -spec reset_envs(config()) -> config(). reset_envs(Config) -> Config#config{envs = new_env()}. -spec set_skip_dir(config(), file:filename()) -> config(). set_skip_dir(Config, Dir) -> OldSkipDirs = Config#config.skip_dirs, NewSkipDirs = case is_skip_dir(Config, Dir) of false -> ?DEBUG("Adding skip dir: ~s\n", [Dir]), dict:store(Dir, true, OldSkipDirs); true -> OldSkipDirs end, Config#config{skip_dirs = NewSkipDirs}. -spec is_skip_dir(config(), file:filename()) -> boolean(). is_skip_dir(Config, Dir) -> dict:is_key(Dir, Config#config.skip_dirs). -spec reset_skip_dirs(config()) -> config(). reset_skip_dirs(Config) -> Config#config{skip_dirs = new_skip_dirs()}. -spec set_xconf(config(), term(), term()) -> config(). set_xconf(Config, Key, Value) -> NewXconf = dict:store(Key, Value, Config#config.xconf), Config#config{xconf=NewXconf}. -spec get_xconf(config(), term()) -> term(). get_xconf(Config, Key) -> {ok, Value} = dict:find(Key, Config#config.xconf), Value. -spec get_xconf(config(), term(), term()) -> term(). get_xconf(Config, Key, Default) -> case dict:find(Key, Config#config.xconf) of error -> Default; {ok, Value} -> Value end. -spec erase_xconf(config(), term()) -> config(). erase_xconf(Config, Key) -> NewXconf = dict:erase(Key, Config#config.xconf), Config#config{xconf = NewXconf}. %% TODO: reconsider after config inheritance removal/redesign -spec clean_config(config(), config()) -> config(). clean_config(Old, New) -> New#config{opts=Old#config.opts}. %% =================================================================== %% Internal functions %% =================================================================== -spec new(config(), file:filename()) -> config(). new(ParentConfig, ConfName) -> %% Load terms from rebar.config, if it exists Dir = rebar_utils:get_cwd(), ConfigFile = filename:join([Dir, ConfName]), Opts0 = ParentConfig#config.opts, 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, ParentConfig#config{dir = Dir, opts = Opts}. -spec consult_and_eval(file:filename(), file:filename()) -> {ok, term()}. consult_and_eval(File, Script) -> ?DEBUG("Evaluating config script ~p~n", [Script]), ConfigData = try_consult(File), file:script(Script, bs([{'CONFIG', ConfigData}, {'SCRIPT', Script}])). -spec remove_script_ext(file:filename()) -> file:filename(). remove_script_ext(F) -> "tpircs." ++ Rev = lists:reverse(F), lists:reverse(Rev). -spec try_consult(file:filename()) -> term(). 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. -type bs_vars() :: [{term(), term()}]. -spec bs(bs_vars()) -> bs_vars(). bs(Vars) -> lists:foldl(fun({K,V}, Bs) -> erl_eval:add_binding(K, V, Bs) end, erl_eval:new_bindings(), Vars). -spec local_opts(list(), list()) -> list(). local_opts([], Acc) -> lists:reverse(Acc); local_opts([local | _Rest], Acc) -> lists:reverse(Acc); local_opts([Item | Rest], Acc) -> local_opts(Rest, [Item | Acc]). -spec new_globals() -> rebar_dict(). new_globals() -> dict:new(). -spec new_env() -> rebar_dict(). new_env() -> dict:new(). -spec new_skip_dirs() -> rebar_dict(). new_skip_dirs() -> dict:new(). -spec new_xconf() -> rebar_dict(). new_xconf() -> dict:new(). rebar-2.6.0/src/rebar_core.erl000066400000000000000000000631161254103774400162260ustar00rootroot00000000000000%% -*- 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, help/2]). -include("rebar.hrl"). %% =================================================================== %% Internal functions %% =================================================================== -spec help(rebar_config:config(), [atom()]) -> ok. help(ParentConfig, Commands) -> %% get all core modules {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules), {ok, RawCoreModules} = application:get_env(rebar, modules), AppDirModules = proplists:get_value(app_dir, RawCoreModules), RelDirModules = proplists:get_value(rel_dir, RawCoreModules), CoreModules = AnyDirModules ++ AppDirModules ++ RelDirModules, %% get plugin modules Predirs = [], Dir = rebar_utils:get_cwd(), PredirsAssoc = remember_cwd_predirs(Dir, Predirs), Config = maybe_load_local_config(Dir, ParentConfig), {ok, PluginModules} = plugin_modules(Config, PredirsAssoc), AllModules = CoreModules ++ PluginModules, lists:foreach( fun(Cmd) -> ?CONSOLE("==> help ~p~n~n", [Cmd]), CmdModules = select_modules(AllModules, Cmd, []), Modules = select_modules(CmdModules, info, []), lists:foreach(fun(M) -> ?CONSOLE("=== ~p:~p ===~n", [M, Cmd]), M:info(help, Cmd), ?CONSOLE("~n", []) end, Modules) end, Commands). -spec process_commands([atom()], rebar_config:config()) -> ok. process_commands([], ParentConfig) -> AbortTrapped = rebar_config:get_xconf(ParentConfig, abort_trapped, false), case {get_operations(ParentConfig), AbortTrapped} of {0, _} -> %% None of the commands had any effect ?FAIL; {_, true} -> %% An abort was previously trapped ?FAIL; _ -> ok end; process_commands([Command | Rest], ParentConfig) -> %% Reset skip dirs ParentConfig1 = rebar_config:reset_skip_dirs(ParentConfig), Operations = get_operations(ParentConfig1), ParentConfig4 = try %% 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(), {ParentConfig2, _DirSet} = process_dir(rebar_utils:get_cwd(), Command, ParentConfig1, sets:new()), case get_operations(ParentConfig2) of Operations -> %% This command didn't do anything ?CONSOLE("Command '~p' not understood or not applicable~n", [Command]); _ -> ok end, %% TODO: reconsider after config inheritance removal/re-design ParentConfig3 = rebar_config:clean_config(ParentConfig1, ParentConfig2), %% Wipe out vsn cache to avoid invalid hits when %% dependencies are updated rebar_config:set_xconf(ParentConfig3, vsn_cache, dict:new()) catch throw:rebar_abort -> case rebar_config:get_xconf(ParentConfig1, keep_going, false) of false -> ?FAIL; true -> ?WARN("Continuing on after abort: ~p\n", [Rest]), rebar_config:set_xconf(ParentConfig1, abort_trapped, true) end end, process_commands(Rest, ParentConfig4). process_dir(Dir, Command, ParentConfig, DirSet) -> case filelib:is_dir(Dir) of false -> ?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]), {ParentConfig, DirSet}; true -> WouldCd = would_cd_into_dir(Dir, Command, ParentConfig), 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), skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, WouldCd) end. would_cd_into_dir(Dir, Command, Config) -> case would_cd_into_dir1(Dir, Command, Config) of true -> would_cd; false -> would_not_cd end. would_cd_into_dir1(Dir, Command, Config) -> rebar_utils:processing_base_dir(Config, Dir) orelse rebar_config:is_recursive(Config) orelse is_recursive_command(Command, Config) orelse is_generate_in_rel_dir(Command, Dir). %% Check whether the command is part of the built-in (or extended via %% rebar.config) list of default-recursive commands. is_recursive_command(Command, Config) -> {ok, AppCmds} = application:get_env(rebar, recursive_cmds), ConfCmds = rebar_config:get_local(Config, recursive_cmds, []), RecursiveCmds = AppCmds ++ ConfCmds, lists:member(Command, RecursiveCmds). %% If the directory we're about to process contains %% reltool.config[.script] and the command to be applied is %% 'generate', then it's safe to process. We do this to retain the %% behavior of specifying {sub_dirs, ["rel"]} and have "rebar generate" %% pick up rel/reltool.config[.script]. Without this workaround you'd %% have to run "rebar -r generate" (which you don't want to do if you %% have deps or other sub_dirs) or "cd rel && rebar generate". is_generate_in_rel_dir(generate, Dir) -> case rebar_rel_utils:is_rel_dir(Dir) of {true, _} -> true; false -> false end; is_generate_in_rel_dir(_, _) -> false. skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath, {[], undefined}=ModuleSet, WouldCd) -> process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, WouldCd); skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath, {_, File}=ModuleSet, WouldCd) -> case lists:suffix(".app.src", File) orelse lists:suffix(".app", File) of true -> %% .app or .app.src file, check if is_skipped_app skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, WouldCd, File); false -> %% not an app dir, no need to consider apps=/skip_apps= process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, WouldCd) end. skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet, WouldCd, AppFile) -> case rebar_app_utils:is_skipped_app(Config, AppFile) of {Config1, {true, _SkippedApp}} when Command == 'update-deps' -> %% update-deps does its own app skipping. Unfortunately there's no %% way to signal this to rebar_core, so we have to explicitly do it %% here... Otherwise if you use app=, it'll skip the toplevel %% directory and nothing will be updated. process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath, ModuleSet, WouldCd); {Config1, {true, SkippedApp}} -> ?DEBUG("Skipping app: ~p~n", [SkippedApp]), {increment_operations(Config1), DirSet}; {Config1, false} -> process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath, ModuleSet, WouldCd) end. process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, {DirModules, File}, WouldCd) -> Config0 = rebar_config:set_xconf(Config, current_command, Command), %% Get the list of modules for "any dir". This is a catch-all list %% of modules that are processed in addition to modules associated %% with this directory type. These any_dir modules are processed %% 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. {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, File), %% Remember associated pre-dirs (used for plugin lookup) PredirsAssoc = remember_cwd_predirs(Dir, Predirs), %% Get the list of plug-in modules from rebar.config. These %% modules may participate in preprocess and postprocess. {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc), AllModules = Modules ++ PluginModules, {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, Config1, File), AllPredirs = Predirs ++ PluginPredirs, ?DEBUG("Predirs: ~p\n", [AllPredirs]), {Config3, DirSet2} = process_each(AllPredirs, Command, Config2, DirSet, File), %% Make sure the CWD is reset properly; processing the dirs may have %% caused it to change ok = file:set_cwd(Dir), %% Maybe apply command to Dir Config4 = maybe_execute(Dir, Command, Config3, Modules, PluginModules, AllModules, File, WouldCd), %% Mark the current directory as processed DirSet3 = sets:add_element(Dir, DirSet2), %% Invoke 'postprocess' on the modules. This yields a list of other %% directories that should be processed _after_ the current one. {Config5, Postdirs} = acc_modules(AllModules, postprocess, Config4, File), ?DEBUG("Postdirs: ~p\n", [Postdirs]), Res = process_each(Postdirs, Command, Config5, DirSet3, File), %% 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 {config, dirset} as result Res. maybe_execute(Dir, Command, Config, Modules, PluginModules, AllModules, File, would_cd) -> %% Check that this directory is not on the skip list case rebar_config:is_skip_dir(Config, Dir) of true -> %% Do not execute the command on the directory, as some %% module has requested a skip on it. ?INFO("Skipping ~s in ~s\n", [Command, Dir]), Config; false -> %% Check for and get command specific environments {Config1, Env} = setup_envs(Config, Modules), %% Execute any before_command plugins on this directory Config2 = execute_pre(Command, PluginModules, Config1, File, Env), %% Execute the current command on this directory Config3 = execute(Command, AllModules, Config2, File, Env), %% Execute any after_command plugins on this directory execute_post(Command, PluginModules, Config3, File, Env) end; maybe_execute(_Dir, _Command, Config, _Modules, _PluginModules, _AllModules, _File, would_not_cd) -> Config. remember_cwd_predirs(Cwd, Predirs) -> Store = fun(Dir, Dict) -> case dict:find(Dir, Dict) of 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?~n", [Dir, Existing]) end end, lists:foldl(Store, dict:new(), Predirs). maybe_load_local_config(Dir, ParentConfig) -> %% We need to ensure we don't overwrite custom %% config when we are dealing with base_dir. case rebar_utils:processing_base_dir(ParentConfig, Dir) of true -> ParentConfig; false -> rebar_config:new(ParentConfig) end. %% %% Given a list of directories and a set of previously processed directories, %% process each one we haven't seen yet %% process_each([], _Command, Config, DirSet, _File) -> %% reset cached (setup_env) envs Config1 = rebar_config:reset_envs(Config), {Config1, DirSet}; process_each([Dir | Rest], Command, Config, DirSet, File) -> case sets:is_element(Dir, DirSet) of true -> ?DEBUG("Skipping ~s; already processed!\n", [Dir]), process_each(Rest, Command, Config, DirSet, File); false -> {Config1, DirSet2} = process_dir(Dir, Command, Config, DirSet), Config2 = rebar_config:clean_config(Config, Config1), %% reset cached (setup_env) envs Config3 = rebar_config:reset_envs(Config2), process_each(Rest, Command, Config3, DirSet2, File) 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, hook, Modules, Config, ModuleFile, Env). %% %% Execute a command across all applicable modules %% execute(Command, Modules, Config, ModuleFile, Env) -> execute(Command, not_a_hook, Modules, Config, ModuleFile, Env). execute(Command, Type, Modules, Config, ModuleFile, Env) -> case select_modules(Modules, Command, []) of [] -> case Type of hook -> ok; not_a_hook -> ?WARN("'~p' command does not apply to directory ~s\n", [Command, rebar_utils:get_cwd()]) end, Config; TargetModules -> %% Provide some info on where we are Dir = rebar_utils:get_cwd(), ?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]), Config1 = increment_operations(Config), %% Run the available modules apply_hooks(pre_hooks, Config1, Command, Env), case catch(run_modules(TargetModules, Command, Config1, ModuleFile)) of {ok, NewConfig} -> apply_hooks(post_hooks, NewConfig, Command, Env), NewConfig; {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(Config) -> Operations = get_operations(Config), rebar_config:set_xconf(Config, operations, Operations + 1). get_operations(Config) -> rebar_config:get_xconf(Config, operations). update_code_path(Config) -> case rebar_config:get_local(Config, lib_dirs, []) of [] -> no_change; Paths -> LibPaths = expand_lib_dirs(Paths, rebar_utils:get_cwd(), []), ok = code:add_pathsa(LibPaths), %% track just the paths we added, so we can remove them without %% removing other paths added by this dep {added, LibPaths} end. restore_code_path(no_change) -> ok; restore_code_path({added, Paths}) -> %% Verify that all of the paths still exist -- some dynamically %% added paths can get blown away during clean. _ = [code:del_path(F) || F <- Paths, erl_prim_loader_is_file(F)], ok. erl_prim_loader_is_file(File) -> erl_prim_loader:read_file_info(File) =/= error. expand_lib_dirs([], _Root, Acc) -> Acc; expand_lib_dirs([Dir | Rest], Root, Acc) -> Apps = filelib:wildcard(filename:join([Dir, "*", "ebin"])), FqApps = case filename:pathtype(Dir) of absolute -> Apps; _ -> [filename:join([Root, A]) || A <- Apps] end, 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, Config}; run_modules([Module | Rest], Command, Config, File) -> case Module:Command(Config, File) of ok -> run_modules(Rest, Command, Config, File); {ok, NewConfig} -> run_modules(Rest, Command, NewConfig, File); {error, _} = Error -> {Module, Error} end. apply_hooks(Mode, Config, Command, Env0) -> Hooks = rebar_config:get_local(Config, Mode, []), Env = rebar_utils:patch_env(Config, Env0), 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:save_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) -> {Config, Acc}; acc_modules([Module | Rest], Command, Config, File, Acc) -> {Config1, Dirs1} = case Module:Command(Config, File) of {ok, Dirs} -> {Config, Dirs}; {ok, NewConfig, Dirs} -> {NewConfig, Dirs} end, acc_modules(Rest, Command, Config1, File, Acc ++ Dirs1). %% %% Return a flat list of rebar plugin modules. %% plugin_modules(Config, PredirsAssoc) -> Modules = lists:flatten(rebar_config:get_all(Config, plugins)), ?DEBUG("Plugins requested while processing ~s: ~p~n", [rebar_utils:get_cwd(), Modules]), plugin_modules(Config, PredirsAssoc, 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, _PredirsAssoc, []) -> {ok, []}; plugin_modules(Config, PredirsAssoc, Modules) -> FoundModules = [M || M <- Modules, code:which(M) =/= non_existing], plugin_modules(Config, PredirsAssoc, FoundModules, Modules -- FoundModules). plugin_modules(_Config, _PredirsAssoc, FoundModules, []) -> {ok, FoundModules}; plugin_modules(Config, PredirsAssoc, FoundModules, MissingModules) -> {Loaded, NotLoaded} = load_plugin_modules(Config, PredirsAssoc, 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, PredirsAssoc, Modules) -> Cwd = rebar_utils:get_cwd(), PluginDirs = get_all_plugin_dirs(Config, Cwd, PredirsAssoc), ?DEBUG("Plugin dirs for ~s:~n~p~n", [Cwd, PluginDirs]), %% Find relevant sources in base_dir and plugin_dir RE = string:join([atom_to_list(M)++"\\.erl" || M <- Modules], "|"), %% 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(PD, RE, false) || PD <- PluginDirs], %% Compile and load plugins Loaded = [load_plugin(Src) || Src <- lists:append(Sources)], FilterMissing = is_missing_plugin(Loaded), NotLoaded = [V || V <- Modules, FilterMissing(V)], {Loaded, NotLoaded}. get_all_plugin_dirs(Config, Cwd, PredirsAssoc) -> [rebar_utils:get_cwd()] ++ get_plugin_dir(Config, Cwd) ++ get_base_plugin_dirs(Cwd, PredirsAssoc). get_plugin_dir(Config, Cwd) -> case rebar_config:get_local(Config, plugin_dir, undefined) of undefined -> %% Plugin can be in the project's "plugins" folder [filename:join(Cwd, "plugins")]; Dir -> [Dir] end. %% We also want to include this case: %% Plugin can be in "plugins" directory of the plugin base directory. %% For example, Cwd depends on Plugin, and deps/Plugin/plugins/Plugin.erl %% is the plugin. get_base_plugin_dirs(Cwd, PredirsAssoc) -> [filename:join(Dir, "plugins") || Dir <- get_plugin_base_dirs(Cwd, PredirsAssoc)]. %% @doc PredirsAssoc is a dictionary of plugindir -> 'parent' pairs. %% 'parent' in this case depends on plugin; therefore we have to give %% all plugins that Cwd ('parent' in this case) depends on. get_plugin_base_dirs(Cwd, PredirsAssoc) -> [PluginDir || {PluginDir, Master} <- dict:to_list(PredirsAssoc), Master =:= Cwd]. 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.6.0/src/rebar_cover_utils.erl000066400000000000000000000257411254103774400176360ustar00rootroot00000000000000%% -*- 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) %% Copyright (c) 2013 Andras Horvath (andras.horvath@erlang-solutions.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_cover_utils). %% for internal use only -export([init/3, perform_cover/4, close/1, exit/0]). -include("rebar.hrl"). %% ==================================================================== %% Internal functions %% ==================================================================== perform_cover(Config, BeamFiles, SrcModules, TargetDir) -> perform_cover(rebar_config:get(Config, cover_enabled, false), Config, BeamFiles, SrcModules, TargetDir). perform_cover(false, _Config, _BeamFiles, _SrcModules, _TargetDir) -> ok; perform_cover(true, Config, BeamFiles, SrcModules, TargetDir) -> analyze(Config, BeamFiles, SrcModules, TargetDir). close(not_enabled) -> ok; close(F) -> ok = file:close(F). exit() -> cover:stop(). init(false, _BeamFiles, _TargetDir) -> {ok, not_enabled}; init(true, BeamFiles, TargetDir) -> %% Attempt to start the cover server, then set its group leader to %% TargetDir/cover.log, so all cover log messages will go there instead of %% to stdout. If the cover server is already started, we'll kill that %% server and start a new one in order not to inherit a polluted %% cover_server state. {ok, CoverPid} = case whereis(cover_server) of undefined -> cover:start(); _ -> cover:stop(), cover:start() end, {ok, F} = OkOpen = file:open( filename:join([TargetDir, "cover.log"]), [write]), group_leader(F, CoverPid), ?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; init(Config, BeamFiles, TargetDir) -> init(rebar_config:get(Config, cover_enabled, false), BeamFiles, TargetDir). analyze(_Config, [], _SrcModules, _TargetDir) -> ok; analyze(Config, FilteredModules, SrcModules, TargetDir) -> %% Generate coverage info for all the cover-compiled modules Coverage = lists:flatten([analyze_mod(M) || M <- FilteredModules, cover:is_compiled(M) =/= false]), %% Write index of coverage info write_index(lists:sort(Coverage), SrcModules, TargetDir), %% Write coverage details for each file lists:foreach( fun({M, _, _}) -> {ok, _} = cover:analyze_to_file(M, cover_file(M, TargetDir), [html]) end, Coverage), Index = filename:join([rebar_utils:get_cwd(), TargetDir, "index.html"]), ?CONSOLE("Cover analysis: ~s\n", [Index]), %% Export coverage data, if configured case rebar_config:get(Config, cover_export_enabled, false) of true -> export_coverdata(TargetDir); false -> ok end, %% Print coverage report, if configured case rebar_config:get(Config, cover_print_enabled, false) of true -> print_coverage(lists:sort(Coverage)); false -> ok end, %% Generate JSON Coverage Data, if configured case rebar_config:get(Config, cover_export_json, false) of true -> export_json_coverage(TargetDir, lists:sort(Coverage)); false -> ok end. 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}. write_index(Coverage, SrcModules, TargetDir) -> {ok, F} = file:open(filename:join([TargetDir, "index.html"]), [write]), ok = file:write(F, "\n" "" "Coverage Summary\n" "\n"), IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end, {SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage), write_index_section(F, "Source", SrcCoverage), write_index_section(F, "Test", TestCoverage), ok = file:write(F, ""), ok = file:close(F). write_index_section(_F, _SectionName, []) -> ok; 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"). 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 : ~4s~n", [Width, Mod, percentage(C, N)]) end, Coverage), ?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]). export_json_coverage(TargetDir,Coverage) -> ?CONSOLE("~nCode Coverage export to json~n", []), lists:foreach(fun(ModuleCoverage) -> export_json_coverage_to_file( TargetDir, ModuleCoverage) end, Coverage). export_json_coverage_to_file(TargetDir, {Module, Covered, NotCovered}) -> {ok, JsonFile} = file:open(json_file(TargetDir, Module), [write]), io:format(JsonFile, "{\"module\":~p,\"covered\":~p,\"not_covered\":~p}", [atom_to_list(Module), Covered, NotCovered]), ok = file:close(JsonFile). json_file(TargetDir, Module) -> filename:join([TargetDir, atom_to_list(Module) ++ ".COVER.json"]). cover_file(Module, TargetDir) -> filename:join([TargetDir, atom_to_list(Module) ++ ".COVER.html"]). export_coverdata(TargetDir) -> ExportFile = filename:join(TargetDir, "cover.coverdata"), case cover:export(ExportFile) of ok -> ?CONSOLE("Coverdata export: ~s~n", [ExportFile]); {error, Reason} -> ?ERROR("Coverdata export failed: ~p~n", [Reason]) end. percentage(0, 0) -> "not executed"; percentage(Cov, NotCov) -> integer_to_list(trunc((Cov / (Cov + NotCov)) * 100)) ++ "%". rebar-2.6.0/src/rebar_ct.erl000066400000000000000000000353531254103774400157060ustar00rootroot00000000000000%% -*- 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 - run common test suites in ./test %% int_test - run suites in ./int_test %% perf_test - run suites inm ./perf_test %% %% Global options: %% verbose=1 - show output from the common_test run as it goes %% suites="foo,bar" - run /foo_SUITE and /bar_SUITE %% case="mycase" - run individual test case foo_SUITE:mycase %% ------------------------------------------------------------------- -module(rebar_ct). -export([ct/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== ct(Config, File) -> TestDir = rebar_config:get_local(Config, ct_dir, "test"), LogDir = rebar_config:get_local(Config, ct_log_dir, "logs"), run_test_if_present(TestDir, LogDir, Config, File). %% =================================================================== %% Internal functions %% =================================================================== info(help, ct) -> ?CONSOLE( "Run common_test suites.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" "Valid command line options:~n" " suites=Suite1,Suite2,...,SuiteN~n" " - run Suite1_SUITE, Suite2_SUITE, ..., SuiteN_SUITE~n" " in the test folder.~n" " groups=Group1,Group2,...,GroupN~n" " - run test groups Group1, Group2, ..., GroupN of specified suites.~n" " cases=Case1,Case2,...,CaseM~n" " - run test cases Case1, Case2, ..., CaseN of specified suites.~n" " case=\"mycase\" - run individual test case Suite1_SUITE:mycase.~n" " This option is deprecated and remains for backward compability.~n" " It is recommended to use 'cases' instead.~n", [ {ct_dir, "itest"}, {ct_log_dir, "test/logs"}, {ct_extra_params, "-boot start_sasl -s myapp"}, {ct_use_short_names, true} ]). run_test_if_present(TestDir, LogDir, Config, File) -> case filelib:is_dir(TestDir) of false -> ?WARN("~s directory not present - skipping\n", [TestDir]), ok; true -> case filelib:wildcard(TestDir ++ "/*_SUITE.{beam,erl}") of [] -> ?WARN("~s directory present, but no common_test" ++ " SUITES - skipping\n", [TestDir]), ok; _ -> try run_test(TestDir, LogDir, Config, File) catch throw:skip -> ok end end end. run_test(TestDir, LogDir, Config, _File) -> {Cmd, RawLog} = make_cmd(TestDir, LogDir, Config), ?DEBUG("ct_run cmd:~n~p~n", [Cmd]), clear_log(LogDir, RawLog), Output = case rebar_log:is_verbose(Config) of false -> " >> " ++ RawLog ++ " 2>&1"; true -> " 2>&1 | tee -a " ++ RawLog end, ShOpts = [{env,[{"TESTDIR", TestDir}]}, return_on_error], case rebar_utils:sh(Cmd ++ Output, ShOpts) of {ok,_} -> %% in older versions of ct_run, this could have been a failure %% that returned a non-0 code. Check for that! check_success_log(Config, RawLog); {error,Res} -> %% In newer ct_run versions, this may be a sign of a good compile %% that failed cases. In older version, it's a worse error. check_fail_log(Config, RawLog, Cmd ++ Output, Res) end. clear_log(LogDir, RawLog) -> case filelib:ensure_dir(filename:join(LogDir, "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_success_log(Config, RawLog) -> check_log(Config, RawLog, fun(Msg) -> ?CONSOLE("DONE.\n~s\n", [Msg]) end). -type err_handler() :: fun((string()) -> no_return()). -spec failure_logger(string(), {integer(), string()}) -> err_handler(). failure_logger(Command, {Rc, Output}) -> fun(_Msg) -> ?ABORT("~s failed with error: ~w and output:~n~s~n", [Command, Rc, Output]) end. check_fail_log(Config, RawLog, Command, Result) -> check_log(Config, RawLog, failure_logger(Command, Result)). check_log(Config,RawLog,Fun) -> {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(Config, RawLog), ?ERROR("Building tests failed\n",[]), ?FAIL; RunFailed -> show_log(Config, RawLog), ?ERROR("One or more tests failed\n",[]), ?FAIL; true -> Fun(Msg) end. %% Show the log if it hasn't already been shown because verbose was on show_log(Config, RawLog) -> ?CONSOLE("Showing log\n", []), case rebar_log:is_verbose(Config) of false -> {ok, Contents} = file:read_file(RawLog), ?CONSOLE("~s", [Contents]); true -> ok end. make_cmd(TestDir, RawLogDir, Config) -> Cwd = rebar_utils:get_cwd(), LogDir = filename:join(Cwd, RawLogDir), EbinDir = filename:absname(filename:join(Cwd, "ebin")), IncludeDir = filename:join(Cwd, "include"), Include = case filelib:is_dir(IncludeDir) of true -> " -include \"" ++ IncludeDir ++ "\""; false -> "" end, %% Check for the availability of ct_run; if we can't find it, generate a %% warning and use the old school, less reliable approach to running CT. BaseCmd = case os:find_executable("ct_run") of false -> "erl -noshell -s ct_run script_start -s erlang halt"; _ -> "ct_run -noshell" 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(Config, Cwd) of undefined -> ?FMT("~s" " -pa ~s" " ~s" " ~s" " -logdir \"~s\"" " -env TEST_DIR \"~s\"", [BaseCmd, CodePathString, Include, build_name(Config), LogDir, filename:join(Cwd, TestDir)]) ++ get_cover_config(Config, Cwd) ++ get_ct_config_file(TestDir) ++ get_suites(Config, TestDir) ++ get_groups(Config) ++ get_cases(Config) ++ get_extra_params(Config) ++ get_config_file(TestDir); SpecFlags -> ?FMT("~s" " -pa ~s" " ~s" " ~s" " -logdir \"~s\"" " -env TEST_DIR \"~s\"", [BaseCmd, CodePathString, Include, build_name(Config), LogDir, filename:join(Cwd, TestDir)]) ++ SpecFlags ++ get_cover_config(Config, Cwd) ++ get_extra_params(Config) end, RawLog = filename:join(LogDir, "raw.log"), {Cmd, RawLog}. build_name(Config) -> case rebar_config:get_local(Config, ct_use_short_names, false) of true -> "-sname test"; false -> " -name test@" ++ net_adm:localhost() end. get_extra_params(Config) -> case rebar_config:get_local(Config, ct_extra_params, undefined) of undefined -> ""; Defined -> " " ++ Defined end. get_ct_specs(Config, Cwd) -> case collect_glob(Config, 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(Config, Cwd, ".*cover\.spec\$") of [] -> ?DEBUG("No cover spec found: ~s~n", [Cwd]), ""; [Spec] -> ?DEBUG("Found cover file ~s~n", [Spec]), " -cover " ++ Spec; Specs -> ?ABORT("Multiple cover specs found: ~p~n", [Specs]) end end. collect_glob(Config, Cwd, Glob) -> {true, Deps} = rebar_deps:get_deps_dir(Config), DepsDir = filename:basename(Deps), CwdParts = filename:split(Cwd), filelib:fold_files( Cwd, Glob, true, fun(F, Acc) -> %% Ignore any specs under the deps/ directory. Do this pulling %% the dirname off the F and then splitting it into a list. Parts = filename:split(filename:dirname(F)), Parts2 = remove_common_prefix(Parts, CwdParts), case lists:member(DepsDir, Parts2) of true -> %% There is a directory named "deps" in path Acc; false -> %% No "deps" directory in path [F | Acc] end end, []). remove_common_prefix([H1|T1], [H1|T2]) -> remove_common_prefix(T1, T2); remove_common_prefix(L1, _) -> L1. 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 -> " -erl_args -config " ++ Config end. get_suites(Config, TestDir) -> case get_suites(Config) of undefined -> " -dir " ++ TestDir; Suites -> Suites1 = [find_suite_path(Suite, TestDir) || Suite <- Suites], string:join([" -suite"] ++ Suites1, " ") end. get_suites(Config) -> case rebar_config:get_global(Config, suites, undefined) of undefined -> %% The option 'suite' is deprecated and remains %% for backward compatibility. %% It is recommended to use 'suites' instead. case get_deprecated_global(Config, suite, suites) of undefined -> undefined; Suite -> [Suite] end; Suites -> string:tokens(Suites, ",") end. find_suite_path(Suite, TestDir) -> Path = filename:join(TestDir, Suite ++ "_SUITE.erl"), case filelib:is_regular(Path) of false -> ?WARN("Suite ~s not found\n", [Suite]), %% Note - this throw is caught in run_test_if_present/3; %% this solution was easier than refactoring the entire module. throw(skip); true -> Path end. get_groups(Config) -> case rebar_config:get_global(Config, groups, undefined) of undefined -> %% The option 'group' was added only for consistency %% because there are options 'suite' and 'case'. case get_deprecated_global(Config, group, groups) of undefined -> ""; Group -> " -group " ++ Group end; Groups -> Groups1 = string:tokens(Groups, ","), string:join([" -group"] ++ Groups1, " ") end. get_cases(Config) -> case rebar_config:get_global(Config, cases, undefined) of undefined -> %% The option 'case' is deprecated and remains %% for backward compatibility. %% It is recommended to use 'cases' instead. case get_deprecated_global(Config, 'case', cases) of undefined -> ""; Case -> " -case " ++ Case end; Cases -> Cases1 = string:tokens(Cases, ","), string:join([" -case"] ++ Cases1, " ") end. get_deprecated_global(Config, OldOpt, NewOpt) -> rebar_utils:get_deprecated_global( Config, OldOpt, NewOpt, undefined, "in the future"). rebar-2.6.0/src/rebar_deps.erl000066400000000000000000001054561254103774400162350ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). -export([get_deps_dir/1]). -record(dep, { dir, app, vsn_regex, source, is_raw }). %% is_raw = true means non-Erlang/OTP dependency %% =================================================================== %% 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 Config1 = set_shared_deps_dir(Config, get_shared_deps_dir(Config, [])), %% Get the list of deps for the current working directory and identify those %% deps that are available/present. Deps = rebar_config:get_local(Config1, deps, []), {Config2, {AvailableDeps, MissingDeps}} = find_deps(Config1, find, Deps), ?DEBUG("Available deps: ~p\n", [AvailableDeps]), ?DEBUG("Missing deps : ~p\n", [MissingDeps]), %% Add available deps to code path Config3 = update_deps_code_path(Config2, AvailableDeps), %% Filtering out 'raw' dependencies so that no commands other than %% deps-related can be executed on their directories. NonRawAvailableDeps = [D || D <- AvailableDeps, not D#dep.is_raw], case rebar_config:get_xconf(Config, current_command, undefined) of 'update-deps' -> %% Skip ALL of the dep folders, we do this because we don't want %% any other calls to preprocess() for update-deps beyond the %% toplevel directory. They aren't actually harmful, but they slow %% things down unnecessarily. NewConfig = lists:foldl( fun(D, Acc) -> rebar_config:set_skip_dir(Acc, D#dep.dir) end, Config3, collect_deps(rebar_utils:get_cwd(), Config3)), %% Return the empty list, as we don't want anything processed before %% us. {ok, NewConfig, []}; _ -> %% 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. %% %% Also, if skip_deps=comma,separated,app,list, then only the given %% dependencies are skipped. NewConfig = case rebar_config:get_global(Config3, skip_deps, false) of "true" -> lists:foldl( fun(#dep{dir = Dir}, C) -> rebar_config:set_skip_dir(C, Dir) end, Config3, AvailableDeps); Apps when is_list(Apps) -> SkipApps = [list_to_atom(App) || App <- string:tokens(Apps, ",")], lists:foldl( fun(#dep{dir = Dir, app = App}, C) -> case lists:member(App, SkipApps) of true -> rebar_config:set_skip_dir(C, Dir); false -> C end end, Config3, AvailableDeps); _ -> Config3 end, %% Return all the available dep directories for process {ok, NewConfig, dep_dirs(NonRawAvailableDeps)} end. postprocess(Config, _) -> case rebar_config:get_xconf(Config, ?MODULE, undefined) of undefined -> {ok, []}; Dirs -> NewConfig = rebar_config:erase_xconf(Config, ?MODULE), {ok, NewConfig, Dirs} end. compile(Config, _) -> {Config1, _AvailDeps} = do_check_deps(Config), {ok, Config1}. %% set REBAR_DEPS_DIR and ERL_LIBS environment variables setup_env(Config) -> {true, DepsDir} = get_deps_dir(Config), %% 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]. %% common function used by 'check-deps' and 'compile' do_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(Config, find, Deps) of {Config1, {AvailDeps, []}} -> %% No missing deps {Config1, AvailDeps}; {_Config1, {_, 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. 'check-deps'(Config, _) -> {Config1, AvailDeps} = do_check_deps(Config), {ok, save_dep_dirs(Config1, AvailDeps)}. 'get-deps'(Config, _) -> %% Determine what deps are available and missing Deps = rebar_config:get_local(Config, deps, []), {Config1, {_AvailableDeps, MissingDeps}} = find_deps(Config, find, Deps), MissingDeps1 = [D || D <- MissingDeps, D#dep.source =/= undefined], %% For each missing dep with a specified source, try to pull it. {Config2, PulledDeps} = lists:foldl(fun(D, {C, PulledDeps0}) -> {C1, D1} = use_source(C, D), {C1, [D1 | PulledDeps0]} end, {Config1, []}, MissingDeps1), %% Add each pulled dep to our list of dirs for post-processing. This yields %% the necessary transitivity of the deps {ok, save_dep_dirs(Config2, lists:reverse(PulledDeps))}. 'update-deps'(Config, _) -> Config1 = rebar_config:set_xconf(Config, depowner, dict:new()), {Config2, UpdatedDeps} = update_deps_int(Config1, []), DepOwners = rebar_config:get_xconf(Config2, depowner, dict:new()), %% check for conflicting deps _ = [?ERROR("Conflicting dependencies for ~p: ~p~n", [K, [{"From: " ++ string:join(dict:fetch(D, DepOwners), ", "), {D#dep.vsn_regex, D#dep.source}} || D <- V]]) || {K, V} <- dict:to_list( lists:foldl( fun(Dep, Acc) -> dict:append(Dep#dep.app, Dep, Acc) end, dict:new(), UpdatedDeps)), length(V) > 1], %% Add each updated dep to our list of dirs for post-processing. This yields %% the necessary transitivity of the deps {ok, save_dep_dirs(Config, UpdatedDeps)}. 'delete-deps'(Config, _) -> %% Delete all the available deps in our deps/ directory, if any {true, DepsDir} = get_deps_dir(Config), Deps = rebar_config:get_local(Config, deps, []), {Config1, {AvailableDeps, _}} = find_deps(Config, find, Deps), _ = [delete_dep(D) || D <- AvailableDeps, lists:prefix(DepsDir, D#dep.dir)], {ok, Config1}. 'list-deps'(Config, _) -> Deps = rebar_config:get_local(Config, deps, []), case find_deps(Config, find, Deps) of {Config1, {AvailDeps, []}} -> lists:foreach(fun(Dep) -> print_source(Dep) end, AvailDeps), {ok, save_dep_dirs(Config1, AvailDeps)}; {_, MissingDeps} -> ?ABORT("Missing dependencies: ~p\n", [MissingDeps]) end. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> info_help("Display to be fetched dependencies"); info(help, 'check-deps') -> info_help("Display to be fetched dependencies"); info(help, 'get-deps') -> info_help("Fetch dependencies"); info(help, 'update-deps') -> info_help("Update fetched dependencies"); info(help, 'delete-deps') -> info_help("Delete fetched dependencies"); info(help, 'list-deps') -> info_help("List dependencies"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" "Valid command line options:~n" " deps_dir=\"deps\" (override default or rebar.config deps_dir)~n", [ Description, {deps_dir, "deps"}, {deps, [app_name, {rebar, "1.0.*"}, {rebar, ".*", {git, "git://github.com/rebar/rebar.git"}}, {rebar, ".*", {git, "git://github.com/rebar/rebar.git", "Rev"}}, {rebar, "1.0.*", {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, {rebar, "1.0.0", {git, "git://github.com/rebar/rebar.git", {tag, "1.0.0"}}}, {rebar, "", {git, "git://github.com/rebar/rebar.git", {branch, "master"}}, [raw]}, {app_name, ".*", {hg, "https://www.example.org/url"}}, {app_name, ".*", {rsync, "Url"}}, {app_name, ".*", {svn, "https://www.example.org/url"}}, {app_name, ".*", {svn, "svn://svn.example.org/url"}}, {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}}, {app_name, ".*", {fossil, "https://www.example.org/url"}}, {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}, {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]} ]). %% Added because of trans deps, %% need all deps in same dir and should be the one set by the root rebar.config %% In case one is given globally, it has higher priority %% Sets a default if root config has no deps_dir set set_shared_deps_dir(Config, []) -> LocalDepsDir = rebar_config:get_local(Config, deps_dir, "deps"), GlobalDepsDir = rebar_config:get_global(Config, deps_dir, LocalDepsDir), DepsDir = case os:getenv("REBAR_DEPS_DIR") of false -> GlobalDepsDir; Dir -> Dir end, rebar_config:set_xconf(Config, deps_dir, DepsDir); set_shared_deps_dir(Config, _DepsDir) -> Config. get_shared_deps_dir(Config, Default) -> rebar_config:get_xconf(Config, deps_dir, Default). get_deps_dir(Config) -> get_deps_dir(Config, ""). get_deps_dir(Config, App) -> BaseDir = rebar_utils:base_dir(Config), DepsDir0 = get_shared_deps_dir(Config, "deps"), DepsDir = filename:dirname(filename:join([BaseDir, DepsDir0, "dummy"])), {true, filename:join([DepsDir, App])}. dep_dirs(Deps) -> [D#dep.dir || D <- Deps]. save_dep_dirs(Config, Deps) -> rebar_config:set_xconf(Config, ?MODULE, dep_dirs(Deps)). 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(Config, []) -> Config; update_deps_code_path(Config, [Dep | Rest]) -> Config2 = case is_app_available(Config, Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir, Dep#dep.is_raw) of {Config1, {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), Config1; {Config1, {false, _}} -> Config1 end, update_deps_code_path(Config2, Rest). find_deps(Config, find=Mode, Deps) -> find_deps(Config, Mode, Deps, {[], []}); find_deps(Config, read=Mode, Deps) -> find_deps(Config, Mode, Deps, []). find_deps(Config, find, [], {Avail, Missing}) -> {Config, {lists:reverse(Avail), lists:reverse(Missing)}}; find_deps(Config, read, [], Deps) -> {Config, lists:reverse(Deps)}; find_deps(Config, Mode, [App | Rest], Acc) when is_atom(App) -> find_deps(Config, Mode, [{App, ".*", undefined} | Rest], Acc); find_deps(Config, Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) -> find_deps(Config, Mode, [{App, VsnRegex, undefined} | Rest], Acc); find_deps(Config, Mode, [{App, VsnRegex, Source} | Rest], Acc) -> find_deps(Config, Mode, [{App, VsnRegex, Source, []} | Rest], Acc); find_deps(Config, Mode, [{App, VsnRegex, Source, Opts} | Rest], Acc) when is_list(Opts) -> Dep = #dep { app = App, vsn_regex = VsnRegex, source = Source, %% dependency is considered raw (i.e. non-Erlang/OTP) when %% 'raw' option is present is_raw = proplists:get_value(raw, Opts, false) }, {Config1, {Availability, FoundDir}} = find_dep(Config, Dep), find_deps(Config1, Mode, Rest, acc_deps(Mode, Availability, Dep, FoundDir, Acc)); find_deps(_Config, _Mode, [Other | _Rest], _Acc) -> ?ABORT("Invalid dependency specification ~p in ~s\n", [Other, rebar_utils:get_cwd()]). find_dep(Config, 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(Config, Dep, Dep#dep.source). find_dep(Config, 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(Config, Dep, get_deps_dir(Config, Dep#dep.app)) of {_Config1, {avail, _Dir}} = Avail -> Avail; {Config1, {missing, _}} -> find_dep_in_dir(Config1, Dep, get_lib_dir(Dep#dep.app)) end; find_dep(Config, 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(Config, Dep, get_deps_dir(Config, Dep#dep.app)). find_dep_in_dir(Config, _Dep, {false, Dir}) -> {Config, {missing, Dir}}; find_dep_in_dir(Config, Dep, {true, Dir}) -> App = Dep#dep.app, VsnRegex = Dep#dep.vsn_regex, IsRaw = Dep#dep.is_raw, case is_app_available(Config, App, VsnRegex, Dir, IsRaw) of {Config1, {true, _AppFile}} -> {Config1, {avail, Dir}}; {Config1, {false, _}} -> {Config1, {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. %% IsRaw = false means regular Erlang/OTP dependency %% %% IsRaw = true means non-Erlang/OTP dependency, e.g. the one that does not %% have a proper .app file is_app_available(Config, App, VsnRegex, Path, _IsRaw = false) -> ?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(Config, AppFile) of {Config1, App} -> {Config2, Vsn} = rebar_app_utils:app_vsn(Config1, 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 -> {Config2, {true, Path}}; nomatch -> ?WARN("~s has version ~p; requested regex was ~s\n", [AppFile, Vsn, VsnRegex]), {Config2, {false, {version_mismatch, {AppFile, {expected, VsnRegex}, {has, Vsn}}}}} end; {Config1, OtherApp} -> ?WARN("~s has application id ~p; expected ~p\n", [AppFile, OtherApp, App]), {Config1, {false, {name_mismatch, {AppFile, {expected, App}, {has, OtherApp}}}}} end; false -> case filelib:is_dir(Path) of true -> %% Path is a directory, but it's not an app dir. ?WARN("Directory expected to be an app dir, but no " "app file found ~n" "in ebin/ or src/:~n~s~n", [Path]); false -> %% Path is not a directory, so it cannot be an app dir. %% TODO: maybe we can avoid checking non-existing dirs ?DEBUG("Directory expected to be an app dir, " "but it doesn't exist (yet?):~n~s~n", [Path]) end, {Config, {false, {missing_app_file, Path}}} end; is_app_available(Config, App, _VsnRegex, Path, _IsRaw = true) -> ?DEBUG("is_app_available, looking for Raw Depencency ~p with Path ~p~n", [App, Path]), case filelib:is_dir(Path) of true -> %% TODO: look for version string in /VERSION file? Not clear %% how to detect git/svn/hg/{cmd, ...} settings that can be passed %% to rebar_utils:vcs_vsn/2 to obtain version dynamically {Config, {true, Path}}; false -> ?WARN("Expected ~s to be a raw dependency directory, " "but no directory found.\n", [Path]), {Config, {false, {missing_raw_dependency_directory, Path}}} end. use_source(Config, Dep) -> use_source(Config, Dep, 3). use_source(_Config, Dep, 0) -> ?ABORT("Failed to acquire source from ~p after 3 tries.\n", [Dep#dep.source]); use_source(Config, Dep, Count) -> case filelib:is_dir(Dep#dep.dir) of true -> %% Already downloaded -- verify the versioning matches the regex case is_app_available(Config, Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir, Dep#dep.is_raw) of {Config1, {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), {Config1, Dep}; {_Config1, {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(Config, Dep#dep.app), download_source(TargetDir, Dep#dep.source), use_source(Config, Dep#dep { dir = TargetDir }, Count-1) end. -record(p4_settings, { client=undefined, transport="tcp4:perforce:1666", username, password }). init_p4_settings(Basename) -> #p4_settings{client = case inet:gethostname() of {ok,HostName} -> HostName ++ "-" ++ os:getenv("USER") ++ "-" ++ Basename ++ "-Rebar-automated-download" end}. download_source(AppDir, {p4, Url}) -> download_source(AppDir, {p4, Url, "#head"}); download_source(AppDir, {p4, Url, Rev}) -> download_source(AppDir, {p4, Url, Rev, init_p4_settings(filename:basename(AppDir))}); download_source(AppDir, {p4, Url, _Rev, Settings}) -> ok = filelib:ensure_dir(AppDir), rebar_utils:sh_send("p4 client -i", ?FMT("Client: ~s~n" ++"Description: generated by Rebar~n" ++"Root: ~s~n" ++"View:~n" ++" ~s/... //~s/...~n", [Settings#p4_settings.client, AppDir, Url, Settings#p4_settings.client]), []), rebar_utils:sh(?FMT("p4 -c ~s sync -f", [Settings#p4_settings.client]), []); 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]), []); download_source(AppDir, {fossil, Url}) -> download_source(AppDir, {fossil, Url, ""}); download_source(AppDir, {fossil, Url, Version}) -> Repository = filename:join(AppDir, filename:basename(AppDir) ++ ".fossil"), ok = filelib:ensure_dir(Repository), ok = file:set_cwd(AppDir), rebar_utils:sh(?FMT("fossil clone ~s ~s", [Url, Repository]), [{cd, AppDir}]), rebar_utils:sh(?FMT("fossil open ~s ~s --nested", [Repository, Version]), []). update_source(Config, 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(Config, 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_source1(AppDir, Dep#dep.source), Dep; false -> ?WARN("Skipping update for ~p: " "no VCS directory available!\n", [Dep]), Dep end. update_source1(AppDir, Args) when element(1, Args) =:= p4 -> download_source(AppDir, Args); update_source1(AppDir, {git, Url}) -> update_source1(AppDir, {git, Url, {branch, "HEAD"}}); update_source1(AppDir, {git, Url, ""}) -> update_source1(AppDir, {git, Url, {branch, "HEAD"}}); update_source1(AppDir, {git, _Url, {branch, Branch}}) -> ShOpts = [{cd, AppDir}], rebar_utils:sh("git fetch origin", ShOpts), rebar_utils:sh(?FMT("git checkout -q ~s", [Branch]), ShOpts), rebar_utils:sh( ?FMT("git pull --ff-only --no-rebase -q origin ~s", [Branch]),ShOpts); update_source1(AppDir, {git, _Url, {tag, Tag}}) -> ShOpts = [{cd, AppDir}], rebar_utils:sh("git fetch origin", ShOpts), rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), ShOpts); update_source1(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_source1(AppDir, {svn, _Url, Rev}) -> rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [{cd, AppDir}]); update_source1(AppDir, {hg, _Url, Rev}) -> rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [{cd, AppDir}]); update_source1(AppDir, {bzr, _Url, Rev}) -> rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [{cd, AppDir}]); update_source1(AppDir, {rsync, Url}) -> rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]); update_source1(AppDir, {fossil, Url}) -> update_source1(AppDir, {fossil, Url, ""}); update_source1(AppDir, {fossil, _Url, Version}) -> ok = file:set_cwd(AppDir), rebar_utils:sh("fossil pull", [{cd, AppDir}]), rebar_utils:sh(?FMT("fossil update ~s", [Version]), []). %% Recursively update deps, this is not done via rebar's usual dep traversal as %% that is the wrong order (tips are updated before branches). Instead we do a %% traverse the deps at each level completely before traversing *their* deps. %% This allows updates to actually propogate down the tree, rather than fail to %% flow up the tree, which was the previous behaviour. update_deps_int(Config0, UDD) -> %% Determine what deps are required ConfDir = filename:basename(rebar_utils:get_cwd()), RawDeps = rebar_config:get_local(Config0, deps, []), {Config1, Deps} = find_deps(Config0, read, RawDeps), %% Update each dep UpdatedDeps = [update_source(Config1, D) || D <- Deps, D#dep.source =/= undefined, not lists:member(D, UDD), not should_skip_update_dep(Config1, D) ], lists:foldl(fun(Dep, {Config, Updated}) -> {true, AppDir} = get_deps_dir(Config, Dep#dep.app), Config2 = case has_vcs_dir(element(1, Dep#dep.source), AppDir) of false -> %% If the dep did not exist (maybe it %% was added), clone it. %% We'll traverse ITS deps below and %% clone them if needed. {C1, _D1} = use_source(Config, Dep), C1; true -> Config end, ok = file:set_cwd(AppDir), Config3 = rebar_config:new(Config2), %% track where a dep comes from... DepOwner = dict:append( Dep, ConfDir, rebar_config:get_xconf(Config3, depowner, dict:new())), Config4 = rebar_config:set_xconf(Config3, depowner, DepOwner), {Config5, Res} = update_deps_int(Config4, Updated), {Config5, lists:umerge(lists:sort(Res), lists:sort(Updated))} end, {Config1, lists:umerge(lists:sort(UpdatedDeps), lists:sort(UDD))}, UpdatedDeps). should_skip_update_dep(Config, Dep) -> {true, AppDir} = get_deps_dir(Config, Dep#dep.app), case rebar_app_utils:is_app_dir(AppDir) of false -> false; {true, AppFile} -> case rebar_app_utils:is_skipped_app(Config, AppFile) of {_Config, {true, _SkippedApp}} -> true; _ -> false end end. %% Recursively walk the deps and build a list of them. collect_deps(Dir, C) -> case file:set_cwd(Dir) of ok -> Config = rebar_config:new(C), RawDeps = rebar_config:get_local(Config, deps, []), {Config1, Deps} = find_deps(Config, read, RawDeps), lists:flatten(Deps ++ [begin {true, AppDir} = get_deps_dir( Config1, Dep#dep.app), collect_deps(AppDir, C) end || Dep <- Deps]); _ -> [] end. %% =================================================================== %% 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; Name == fossil; Name == p4 -> 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(p4) -> {2013, 1}; 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}; required_vcs_client_vsn(fossil) -> {1, 0}. vcs_client_vsn(p4) -> vcs_client_vsn(rebar_utils:find_executable("p4"), " -V", "Rev\\. .*/(\\d+)\\.(\\d)/"); 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+)"); vcs_client_vsn(fossil) -> vcs_client_vsn(rebar_utils:find_executable("fossil"), " version", "version (\\d+).(\\d+)"). has_vcs_dir(p4, _) -> true; 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, {p4, Url}) -> format_source(App, {p4, Url, "#head"}); 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, {SrcType, Url}) -> ?FMT("~p ~p ~s", [App, SrcType, Url]); format_source(App, undefined) -> ?FMT("~p", [App]). rebar-2.6.0/src/rebar_dia_compiler.erl000066400000000000000000000125561254103774400177270ustar00rootroot00000000000000%% -*- 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_dia_compiler). -export([compile/2, clean/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== -spec compile(rebar_config:config(), file:filename()) -> 'ok'. compile(Config, _AppFile) -> DiaOpts = rebar_config:get(Config, dia_opts, []), IncludeEbin = proplists:get_value(include, DiaOpts, []), DiaFiles = filelib:wildcard("dia/*.dia"), code:add_pathsz(["ebin" | IncludeEbin]), FileSequence = case rebar_config:get(Config, dia_first_files, []) of [] -> DiaFiles; CompileFirst -> CompileFirst ++ [F || F <- DiaFiles, not lists:member(F, CompileFirst)] end, rebar_base_compiler:run(Config, FileSequence, "dia", ".dia", "src", ".erl", fun compile_dia/3). -spec clean(rebar_config:config(), file:filename()) -> 'ok'. clean(Config, _AppFile) -> DiaOpts = rebar_config:get(Config, dia_opts, []), IncludeEbin = proplists:get_value(include, DiaOpts, []), code:add_pathsz(["ebin" | IncludeEbin]), GeneratedFiles = dia_generated_files("dia", "src", "include"), ok = rebar_file_utils:delete_each(GeneratedFiles), ok. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> info_help("Build Diameter (*.dia) sources"); info(help, clean) -> info_help("Delete generated Diameter files"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " {dia_opts, []} (options from diameter_make:codec/2 supported with~n" " exception of inherits)~n" " {dia_first_files, []} (files in sequence to compile first)~n", [Description]). -spec compile_dia(file:filename(), file:filename(), rebar_config:config()) -> ok. compile_dia(Source, Target, Config) -> ok = filelib:ensure_dir(Target), ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")), Opts = [{outdir, "src"}] ++ rebar_config:get(Config, dia_opts, []), case diameter_dict_util:parse({path, Source}, []) of {ok, Spec} -> FileName = dia_filename(Source, Spec), _ = diameter_codegen:from_dict(FileName, Spec, Opts, erl), _ = diameter_codegen:from_dict(FileName, Spec, Opts, hrl), HrlFile = filename:join("src", FileName ++ ".hrl"), ErlFile = filename:join("src", FileName ++ ".erl"), ErlCOpts = [{outdir, "ebin"}] ++ rebar_config:get(Config, erl_opts, []), _ = compile:file(ErlFile, ErlCOpts), case filelib:is_regular(HrlFile) of true -> ok = rebar_file_utils:mv(HrlFile, "include"); false -> ok end; {error, Reason} -> ?ABORT( "Compiling ~s failed: ~s~n", [Source, diameter_dict_util:format_error(Reason)] ) end. dia_generated_files(DiaDir, SrcDir, IncDir) -> F = fun(File, Acc) -> case catch diameter_dict_util:parse({path, File}, []) of {ok, Spec} -> FileName = dia_filename(File, Spec), [ filename:join([IncDir, FileName ++ ".hrl"]) | filelib:wildcard( filename:join([SrcDir, FileName ++ ".*"]) ) ] ++ Acc; _ -> Acc end end, lists:foldl(F, [], filelib:wildcard(filename:join([DiaDir, "*.dia"]))). dia_filename(File, Spec) -> case proplists:get_value(name, Spec) of undefined -> filename:rootname(filename:basename(File)); Name -> Name end. rebar-2.6.0/src/rebar_dialyzer.erl000066400000000000000000000175351254103774400171250ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014-2015 Tuncer Ayaz %% %% 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_dialyzer). -export([ dialyze/2, 'build-plt'/2, 'check-plt'/2, 'delete-plt'/2 ]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== dialyze(Config, AppFile) -> Opts = opts(Config), {NewConfig, Plt} = plt(Config, AppFile, Opts), ok = check_plt_existence(Plt), Args = [ {analysis_type, succ_typings}, %% http://erlang.org/pipermail/erlang-bugs/2015-February/004781.html %% TODO: remove once the minimum required Erlang/OTP release %% includes a Dialyzer version without the bug, or alternatively %% add a config option to always check the PLT, as this may be %% needed by some users. {check_plt, false}, {init_plt, Plt}, {files_rec, ["ebin"]}, {warnings, warnings(Opts)} ], ?DEBUG("dialyze opts:~n~p~n", [Args]), case run(Args) of [] -> {ok, NewConfig}; Ws -> print_warnings(Ws, fullpath), ?FAIL end. 'build-plt'(Config, AppFile) -> Opts = opts(Config), {Config1, AppDirs} = app_dirs(Config, AppFile, Opts), {NewConfig, Plt} = plt(Config1, AppFile, Opts), Args = [ {analysis_type, plt_build}, {output_plt, Plt}, {files_rec, AppDirs} ], ?DEBUG("build-plt opts:~n~p~n", [Args]), case run(Args) of [] -> {ok, NewConfig}; Ws -> %% As plt_build may raise warnings but still successfully %% create the PLT, we cannot interpret this as failure, %% and therefore all we can do is report warnings. print_warnings(Ws, basename) end. 'check-plt'(Config, AppFile) -> Opts = opts(Config), {NewConfig, Plt} = plt(Config, AppFile, Opts), ok = check_plt_existence(Plt), Args = [ {analysis_type, plt_check}, %% http://erlang.org/pipermail/erlang-bugs/2015-February/004781.html %% Without this, the PLT will be checked twice. %% TODO: remove once the minimum required Erlang/OTP release %% includes a Dialyzer version without the bug. {check_plt, false}, {init_plt, Plt} ], ?DEBUG("build-plt opts:~n~p~n", [Args]), case run(Args) of [] -> {ok, NewConfig}; Ws -> print_warnings(Ws, basename), ?FAIL end. 'delete-plt'(Config, AppFile) -> Opts = opts(Config), {NewConfig, Plt} = plt(Config, AppFile, Opts), ?DEBUG("Delete PLT '~s'~n", [Plt]), ok = rebar_file_utils:delete_each([Plt]), {ok, NewConfig}. %% =================================================================== %% Internal functions %% =================================================================== info(help, dialyze) -> info_help("Analyze the code for discrepancies"); info(help, 'build-plt') -> info_help("Build project-specific PLT"); info(help, 'check-plt') -> info_help("Check the PLT for consistency and rebuild it if it" " is not up-to-date"); info(help, 'delete-plt') -> info_help("Delete project-specific PLT"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n", [ Description, {dialyzer, [ {plt_location, local}, {plt_location, "custom_dir"}, {plt_extra_apps, [app1, app2]}, {warnings, [unmatched_returns, error_handling]} ]} ]). opts(Config) -> rebar_config:get_local(Config, dialyzer, []). plt(Config, AppFile, Opts) -> PltDir = plt_dir(Config, Opts), {NewConfig, RawAppName} = rebar_app_utils:app_name(Config, AppFile), AppName = atom_to_list(RawAppName), OtpRel = rebar_utils:otp_release(), Plt = filename:join([PltDir, AppName ++ "_" ++ OtpRel ++ "_plt"]), ok = filelib:ensure_dir(Plt), {NewConfig, Plt}. plt_dir(Config, Opts) -> Location = proplists:get_value(plt_location, Opts, local), plt_dir1(Config, Location). plt_dir1(_Config, Location) when is_list(Location) -> case filelib:is_dir(Location) of false -> ?ABORT("PLT directory does not exist: ~s~n", [Location]); true -> Location end; plt_dir1(Config, local) -> BaseDir = rebar_utils:base_dir(Config), filename:join([BaseDir, ".rebar"]). check_plt_existence(Plt) -> case filelib:is_regular(Plt) of true -> ok; false -> ?ABORT("PLT '~s' does not exist.~n" "Please run 'rebar build-plt' first.~n", [Plt]) end. %% dialyzer:run/1 wrapper to gracefully fail in case of Dialyzer errors run(Opts) -> try dialyzer:run(Opts) of Ws -> Ws catch throw:{dialyzer_error, Reason} -> ?ABORT("Dialyzer error:~n~s~n", [Reason]) end. warnings(Opts) -> proplists:get_value(warnings, Opts, []). print_warnings(Ws, Option) -> lists:foreach( fun(W) -> ?CONSOLE("~s~n", [format_warning(W, Option)]) end, Ws). format_warning(W, Option) -> case dialyzer:format_warning(W, Option) of ":0: " ++ Unknown -> strip_newline(Unknown); Warning -> strip_newline(Warning) end. %% Warning may or may not have trailing \n. strip_newline(Warning) -> string:strip(Warning, right, $\n). app_dirs(Config, AppFile, Opts) -> {NewConfig, AppFileApps} = app_file_apps(Config, AppFile), ?DEBUG("app file apps:~n~p~n", [AppFileApps]), Deps = deps_apps(Config), ?DEBUG("deps apps:~n~p~n", [Deps]), ExtraApps = proplists:get_value(plt_extra_apps, Opts, []), ?DEBUG("extra apps:~n~p~n", [ExtraApps]), %% erts is assumed, and has to be present unconditionally. Erts = [erts], Apps = ordsets:from_list(Erts ++ AppFileApps ++ Deps ++ ExtraApps), AppDirs = [app_lib_dir(App) || App <- ordsets:to_list(Apps)], ?DEBUG("app dirs:~n~p~n", [AppDirs]), {NewConfig, AppDirs}. app_file_apps(Config, AppFile) -> rebar_app_utils:app_applications(Config, AppFile). deps_apps(Config) -> [element(1, Dep) || Dep <- rebar_config:get_local(Config, deps, [])]. app_lib_dir(App) -> case code:lib_dir(App, ebin) of {error, _}=Err -> ?ABORT("Failed to get ebin dir for app: ~p~n~p~n", [App, Err]); Dir -> Dir end. rebar-2.6.0/src/rebar_edoc.erl000066400000000000000000000117101254103774400162010ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== doc(Config, File) -> %% Save code path CodePath = setup_code_path(), %% Get the edoc_opts and app file info EDocOpts = rebar_config:get(Config, edoc_opts, []), {ok, Config1, AppName, _AppData} = rebar_app_utils:load_app_file(Config, File), case needs_regen(EDocOpts) of true -> ?INFO("Regenerating edocs for ~p\n", [AppName]), ok = edoc:application(AppName, ".", EDocOpts); false -> ?INFO("Skipping regeneration of edocs for ~p\n", [AppName]), ok end, %% Restore code path true = rebar_utils:cleanup_code_path(CodePath), {ok, Config1}. %% =================================================================== %% Internal functions %% =================================================================== info(help, doc) -> ?CONSOLE( "Generate Erlang program documentation.~n" "~n" "Valid rebar.config options:~n" " {edoc_opts, []} (see edoc:application/3 documentation)~n", []). 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(), _ = code:add_patha(rebar_utils:ebin_dir()), CodePath. -type path_spec() :: {'file', file:filename()} | file:filename(). -spec newer_file_exists(Paths::[path_spec()], OldFile::string()) -> boolean(). newer_file_exists(Paths, OldFile) -> OldModTime = filelib:last_modified(OldFile), ThrowIfNewer = fun(Fn, _Acc) -> FModTime = filelib:last_modified(Fn), (FModTime > OldModTime) andalso throw({newer_file_exists, {Fn, FModTime}}) end, try lists:foldl(fun({file, F}, _) -> ThrowIfNewer(F, false); (P, _) -> filelib:fold_files(P, ".*.erl", true, ThrowIfNewer, false) end, undefined, Paths) catch throw:{newer_file_exists, {Filename, FMod}} -> ?DEBUG("~p is more recent than ~p: " "~120p > ~120p\n", [Filename, OldFile, FMod, OldModTime]), true end. %% Needs regen if any dependent file is changed since the last %% edoc run. Dependent files are the erlang source files, %% and the overview file, if it exists. -spec needs_regen(proplists:proplist()) -> boolean(). needs_regen(EDocOpts) -> DocDir = proplists:get_value(dir, EDocOpts, "doc"), EDocInfoName = filename:join(DocDir, "edoc-info"), OverviewFile = proplists:get_value(overview, EDocOpts, "overview.edoc"), EDocOverviewName = filename:join(DocDir, OverviewFile), SrcPaths = proplists:get_value(source_path, EDocOpts, ["src"]), newer_file_exists([{file, EDocOverviewName} | SrcPaths], EDocInfoName). rebar-2.6.0/src/rebar_erlc_compiler.erl000066400000000000000000000703471254103774400201210ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([test_compile/3, info/2]). -include("rebar.hrl"). -include_lib("stdlib/include/erl_compile.hrl"). -define(ERLCINFO_VSN, 2). -define(ERLCINFO_FILE, "erlcinfo"). -type erlc_info_v() :: {digraph:vertex(), term()} | 'false'. -type erlc_info_e() :: {digraph:vertex(), digraph:vertex()}. -type erlc_info() :: {list(erlc_info_v()), list(erlc_info_e()), list(string())}. -record(erlcinfo, { vsn = ?ERLCINFO_VSN :: pos_integer(), info = {[], [], []} :: erlc_info() }). %% =================================================================== %% 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(rebar_config:config(), 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(rebar_config:config(), file:filename()) -> 'ok'. clean(_Config, _AppFile) -> MibFiles = rebar_utils:find_files_by_ext("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_by_ext("src", ".[x|y]rl"), rebar_file_utils:delete_each( [ binary_to_list(iolist_to_binary(re:replace(F, "\\.[x|y]rl$", ".erl"))) || F <- YrlFiles ]), %% Delete the build graph, if any rebar_file_utils:rm_rf(erlcinfo_file()), %% 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_by_ext("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 and qc) %% =================================================================== test_compile(Config, Cmd, OutDir) -> %% Obtain all the test modules for inclusion in the compile stage. TestErls = rebar_utils:find_files_by_ext("test", ".erl"), ErlOpts = rebar_utils:erl_opts(Config), {Config1, ErlOpts1} = test_compile_config_and_opts(Config, ErlOpts, Cmd), %% Copy source files to eunit dir for cover in case they are not directly %% in src but in a subdirectory of src. Cover only looks in cwd and ../src %% for source files. Also copy files from src_dirs. SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts1)), SrcErls = lists:foldl( fun(Dir, Acc) -> Files = rebar_utils:find_files_by_ext(Dir, ".erl"), lists:append(Acc, Files) end, [], SrcDirs), %% If it is not the first time rebar eunit or rebar qc is executed, %% there will be source files already present in OutDir. 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 OutDir 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 OutDir ToCleanUp = fun(F, Acc) -> F2 = filename:basename(F), F3 = filename:join([OutDir, 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, OutDir), %% Compile erlang code to OutDir, using a tweaked config %% with appropriate defines for eunit, and include all the test modules %% as well. ok = doterl_compile(Config1, OutDir, TestErls, ErlOpts1), {ok, SrcErls}. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> info_help("Build *.erl, *.yrl, *.xrl, and *.mib sources"); info(help, clean) -> info_help("Delete *.erl, *.yrl, *.xrl, and *.mib build results"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n", [ Description, {erl_opts, [no_debug_info, {i, "myinclude"}, {src_dirs, ["src", "src2", "src3"]}, {platform_define, "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'}, {platform_define, "(linux|freebsd)", 'BACKLOG', 128}, {platform_define, "R13", 'old_inets'}]}, {erl_first_files, ["src/mymib1.erl", "src/mymib2.erl"]}, {mib_opts, []}, {mib_first_files, []}, {xrl_opts, []}, {xrl_first_files, []}, {yrl_opts, []}, {yrl_first_files, []} ]). test_compile_config_and_opts(Config, ErlOpts, Cmd) -> {Config1, TriqOpts} = triq_opts(Config), {Config2, PropErOpts} = proper_opts(Config1), {Config3, EqcOpts} = eqc_opts(Config2), %% NOTE: For consistency, all *_first_files lists should be %% retrieved via rebar_config:get_local. Right now %% erl_first_files, eunit_first_files, and qc_first_files use %% rebar_config:get_list and are inherited, but xrl_first_files %% and yrl_first_files use rebar_config:get_local. Inheritance of %% *_first_files is questionable as the file would need to exist %% in all project directories for it to work. OptsAtom = list_to_atom(Cmd ++ "_compile_opts"), TestOpts = rebar_config:get_list(Config3, OptsAtom, []), Opts0 = [{d, 'TEST'}] ++ ErlOpts ++ TestOpts ++ TriqOpts ++ PropErOpts ++ EqcOpts, Opts = [O || O <- Opts0, O =/= no_debug_info], Config4 = rebar_config:set(Config3, erl_opts, Opts), FirstFilesAtom = list_to_atom(Cmd ++ "_first_files"), FirstErls = rebar_config:get_list(Config4, FirstFilesAtom, []), Config5 = rebar_config:set(Config4, erl_first_files, FirstErls), {Config5, Opts}. triq_opts(Config) -> {NewConfig, IsAvail} = is_lib_avail(Config, is_triq_avail, triq, "triq.hrl", "Triq"), Opts = define_if('TRIQ', IsAvail), {NewConfig, Opts}. proper_opts(Config) -> {NewConfig, IsAvail} = is_lib_avail(Config, is_proper_avail, proper, "proper.hrl", "PropEr"), Opts = define_if('PROPER', IsAvail), {NewConfig, Opts}. eqc_opts(Config) -> {NewConfig, IsAvail} = is_lib_avail(Config, is_eqc_avail, eqc, "eqc.hrl", "QuickCheck"), Opts = define_if('EQC', IsAvail), {NewConfig, Opts}. define_if(Def, true) -> [{d, Def}]; define_if(_Def, false) -> []. is_lib_avail(Config, DictKey, Mod, Hrl, Name) -> case rebar_config:get_xconf(Config, DictKey, undefined) of undefined -> IsAvail = case code:lib_dir(Mod, include) of {error, bad_name} -> false; Dir -> filelib:is_regular(filename:join(Dir, Hrl)) end, NewConfig = rebar_config:set_xconf(Config, DictKey, IsAvail), ?DEBUG("~s availability: ~p\n", [Name, IsAvail]), {NewConfig, IsAvail}; IsAvail -> {Config, IsAvail} end. -spec doterl_compile(rebar_config:config(), file:filename()) -> 'ok'. doterl_compile(Config, OutDir) -> ErlOpts = rebar_utils:erl_opts(Config), doterl_compile(Config, OutDir, [], ErlOpts). doterl_compile(Config, OutDir, MoreSources, ErlOpts) -> ?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 = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)), AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources, %% 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")), OutDir1 = proplists:get_value(outdir, ErlOpts, OutDir), G = init_erlcinfo(proplists:get_all_values(i, ErlOpts), AllErlFiles), NeededErlFiles = needed_files(G, OutDir1, AllErlFiles), {ErlFirstFiles, ErlOptsFirst} = erl_first_files(Config, ErlOpts, NeededErlFiles), {DepErls, OtherErls} = lists:partition( fun(Source) -> digraph:in_degree(G, Source) > 0 end, [F || F <- NeededErlFiles, not lists:member(F, ErlFirstFiles)]), DepErlsOrdered = digraph_utils:topsort(digraph_utils:subgraph(G, DepErls)), FirstErls = ErlFirstFiles ++ lists:reverse(DepErlsOrdered), ?DEBUG("Files to compile first: ~p~n", [FirstErls]), rebar_base_compiler:run( Config, FirstErls, OtherErls, fun(S, C) -> ErlOpts1 = case lists:member(S, ErlFirstFiles) of true -> ErlOptsFirst; false -> ErlOpts end, internal_erl_compile(C, S, OutDir1, ErlOpts1) end), true = rebar_utils:cleanup_code_path(CurrPath), ok. %% Get files which need to be compiled first, i.e. those specified in %% erl_first_files and parse_transform options. Also produce specific %% erl_opts for these first files, so that yet to be compiled parse %% transformations are excluded from it. erl_first_files(Config, ErlOpts, NeededErlFiles) -> %% NOTE: rebar_config:get_local perhaps? ErlFirstFilesConf = rebar_config:get_list(Config, erl_first_files, []), NeededSrcDirs = lists:usort( lists:map(fun filename:dirname/1, NeededErlFiles)), %% NOTE: order of files here is important! ErlFirstFiles = lists:filter( fun(File) -> lists:member(File, NeededErlFiles) end, ErlFirstFilesConf), {ParseTransforms, ParseTransformsErls} = lists:unzip( lists:flatmap( fun(PT) -> PTerls = [filename:join(Dir, module_to_erl(PT)) || Dir <- NeededSrcDirs], [{PT, PTerl} || PTerl <- PTerls, lists:member(PTerl, NeededErlFiles)] end, proplists:get_all_values(parse_transform, ErlOpts))), ErlOptsFirst = lists:filter( fun ({parse_transform, PT}) -> not lists:member(PT, ParseTransforms); (_) -> true end, ErlOpts), {ErlFirstFiles ++ ParseTransformsErls, ErlOptsFirst}. %% Get subset of SourceFiles which need to be recompiled, respecting %% dependencies induced by given graph G. needed_files(G, OutDir, SourceFiles) -> lists:filter( fun(Source) -> Target = target_base(OutDir, Source) ++ ".beam", digraph:vertex(G, Source) > {Source, filelib:last_modified(Target)} end, SourceFiles). target_base(OutDir, Source) -> filename:join(OutDir, filename:basename(Source, ".erl")). erlcinfo_file() -> filename:join([rebar_utils:get_cwd(), ".rebar", ?ERLCINFO_FILE]). %% Get dependency graph of given Erls files and their dependencies %% (header files, parse transforms, behaviours etc.) located in their %% directories or given InclDirs. Note that last modification times %% stored in vertices already respect %% dependencies induced by given graph G. init_erlcinfo(InclDirs, Erls) -> G = digraph:new([acyclic]), try restore_erlcinfo(G, InclDirs) catch _:_ -> ?WARN("Failed to restore ~s file. Discarding it.~n", [erlcinfo_file()]), ok = file:delete(erlcinfo_file()) end, Dirs = source_and_include_dirs(InclDirs, Erls), Modified = lists:foldl(update_erlcinfo_fun(G, Dirs), false, Erls), if Modified -> store_erlcinfo(G, InclDirs); not Modified -> ok end, G. source_and_include_dirs(InclDirs, Erls) -> SourceDirs = lists:map(fun filename:dirname/1, Erls), lists:usort(["include" | InclDirs ++ SourceDirs]). update_erlcinfo_fun(G, Dirs) -> fun(Erl, Modified) -> case update_erlcinfo(G, Dirs, Erl) of modified -> true; unmodified -> Modified end end. update_erlcinfo(G, Dirs, Source) -> case digraph:vertex(G, Source) of {_, LastUpdated} -> case filelib:last_modified(Source) of 0 -> %% The file doesn't exist anymore, %% erase it from the graph. %% All the edges will be erased automatically. ?WARN("File missing. Remove from dep graph:~n~s~n", [Source]), digraph:del_vertex(G, Source), modified; LastModified when LastUpdated < LastModified -> modify_erlcinfo(G, Source, LastModified, Dirs); _ -> Modified = lists:foldl( update_erlcinfo_fun(G, Dirs), false, digraph:out_neighbours(G, Source)), MaxModified = update_max_modified_deps(G, Source), case Modified orelse MaxModified > LastUpdated of true -> modified; false -> unmodified end end; false -> modify_erlcinfo(G, Source, filelib:last_modified(Source), Dirs) end. update_max_modified_deps(G, Source) -> MaxModified = lists:max( lists:map( fun(File) -> {_, MaxModified} = digraph:vertex(G, File), MaxModified end, [Source|digraph:out_neighbours(G, Source)])), digraph:add_vertex(G, Source, MaxModified), MaxModified. modify_erlcinfo(_G, Source, 0, _Dirs) -> ?WARN("modify_erlcinfo: failed to open file:~n~s~n", [Source]), unmodified; modify_erlcinfo(G, Source, LastModified, Dirs) -> {ok, Fd} = file:open(Source, [read]), Incls = parse_attrs(Fd, []), AbsIncls = expand_file_names(Incls, Dirs), ok = file:close(Fd), digraph:add_vertex(G, Source, LastModified), digraph:del_edges(G, digraph:out_edges(G, Source)), lists:foreach( fun(Incl) -> update_erlcinfo(G, Dirs, Incl), digraph:add_edge(G, Source, Incl) end, AbsIncls), modified. restore_erlcinfo(G, InclDirs) -> case file:read_file(erlcinfo_file()) of {ok, Data} -> %% Since externally passed InclDirs can influence erlcinfo %% graph (see modify_erlcinfo), we have to check here that %% they didn't change. #erlcinfo{vsn=?ERLCINFO_VSN, info={Vs, Es, InclDirs}} = binary_to_term(Data), lists:foreach( fun({V, LastUpdated}) -> digraph:add_vertex(G, V, LastUpdated) end, Vs), lists:foreach( fun({_, V1, V2, _}) -> digraph:add_edge(G, V1, V2) end, Es); {error, _} -> ok end. store_erlcinfo(G, InclDirs) -> Vs = lists:map(fun(V) -> digraph:vertex(G, V) end, digraph:vertices(G)), Es = lists:map(fun(E) -> digraph:edge(G, E) end, digraph:edges(G)), File = erlcinfo_file(), ok = filelib:ensure_dir(File), Info = #erlcinfo{info={Vs, Es, InclDirs}}, Data = term_to_binary(Info, [{compressed, 2}]), ok = file:write_file(File, Data). %% NOTE: If, for example, one of the entries in Files refers to %% gen_server.erl, that entry will be dropped. It is dropped because %% such an entry usually refers to the beam file, and we don't pass a %% list of OTP src dirs for finding gen_server.erl's full path. Also, %% if gen_server.erl was modified, it's not rebar's task to compile a %% new version of the beam file. Therefore, it's reasonable to drop %% such entries. Also see process_attr(behaviour, Form, Includes). -spec expand_file_names([file:filename()], [file:filename()]) -> [file:filename()]. expand_file_names(Files, Dirs) -> %% We check if Files exist by itself or within the directories %% listed in Dirs. %% Return the list of files matched. lists:flatmap( fun(Incl) -> case filelib:is_regular(Incl) of true -> [Incl]; false -> lists:flatmap( fun(Dir) -> FullPath = filename:join(Dir, Incl), case filelib:is_regular(FullPath) of true -> [FullPath]; false -> [] end end, Dirs) end end, Files). -spec internal_erl_compile( rebar_config:config(), file:filename(), file:filename(), list()) -> ok | {ok, any()} | {error, any(), any()}. internal_erl_compile(Config, Source, OutDir, ErlOpts) -> ok = filelib:ensure_dir(OutDir), Opts = [{outdir, OutDir}] ++ ErlOpts ++ [{i, "include"}, return], case compile:file(Source, Opts) of {ok, _Mod} -> ok; {ok, _Mod, Ws} -> rebar_base_compiler:ok_tuple(Config, Source, Ws); {error, Es, Ws} -> rebar_base_compiler:error_tuple(Config, Source, Es, Ws, Opts) end. -spec compile_mib(file:filename(), file:filename(), 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), MibToHrlOpts = case proplists:get_value(verbosity, Opts, undefined) of undefined -> #options{specific = []}; Verbosity -> #options{specific = [{verbosity, Verbosity}]} end, ok = snmpc:mib_to_hrl(Mib, Mib, MibToHrlOpts), Hrl_filename = Mib ++ ".hrl", rebar_file_utils:mv(Hrl_filename, "include"), ok; {error, compilation_failed} -> ?FAIL end. -spec compile_xrl(file:filename(), file:filename(), rebar_config:config()) -> 'ok'. compile_xrl(Source, Target, Config) -> Opts = [{scannerfile, Target} | rebar_config:get(Config, xrl_opts, [])], compile_xrl_yrl(Config, Source, Target, Opts, leex). -spec compile_yrl(file:filename(), file:filename(), rebar_config:config()) -> 'ok'. compile_yrl(Source, Target, Config) -> Opts = [{parserfile, Target} | rebar_config:get(Config, yrl_opts, [])], compile_xrl_yrl(Config, Source, Target, Opts, yecc). -spec compile_xrl_yrl(rebar_config:config(), file:filename(), file:filename(), list(), module()) -> 'ok'. compile_xrl_yrl(Config, Source, Target, Opts, Mod) -> case needs_compile(Source, Target) of true -> case Mod:file(Source, Opts ++ [{return, true}]) of {ok, _} -> ok; {ok, _Mod, Ws} -> rebar_base_compiler:ok_tuple(Config, Source, Ws); {error, Es, Ws} -> rebar_base_compiler:error_tuple(Config, Source, Es, Ws, Opts) end; false -> skipped end. needs_compile(Source, Target) -> filelib:last_modified(Source) > filelib:last_modified(Target). gather_src([], Srcs) -> Srcs; gather_src([Dir|Rest], Srcs) -> gather_src( Rest, Srcs ++ rebar_utils:find_files_by_ext(Dir, ".erl")). -spec dirs(file:filename()) -> [file:filename()]. dirs(Dir) -> [F || F <- filelib:wildcard(filename:join([Dir, "*"])), filelib:is_dir(F)]. -spec delete_dir(file:filename(), [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). parse_attrs(Fd, Includes) -> case io:parse_erl_form(Fd, "") of {ok, Form, _Line} -> case erl_syntax:type(Form) of attribute -> NewIncludes = process_attr(Form, Includes), parse_attrs(Fd, NewIncludes); _ -> parse_attrs(Fd, Includes) end; {eof, _} -> Includes; _Err -> parse_attrs(Fd, Includes) end. process_attr(Form, Includes) -> AttrName = erl_syntax:atom_value(erl_syntax:attribute_name(Form)), process_attr(AttrName, Form, Includes). process_attr(import, Form, Includes) -> case erl_syntax_lib:analyze_import_attribute(Form) of {Mod, _Funs} -> [module_to_erl(Mod)|Includes]; Mod -> [module_to_erl(Mod)|Includes] end; process_attr(file, Form, Includes) -> {File, _} = erl_syntax_lib:analyze_file_attribute(Form), [File|Includes]; process_attr(include, Form, Includes) -> [FileNode] = erl_syntax:attribute_arguments(Form), File = erl_syntax:string_value(FileNode), [File|Includes]; process_attr(include_lib, Form, Includes) -> [FileNode] = erl_syntax:attribute_arguments(Form), RawFile = erl_syntax:string_value(FileNode), maybe_expand_include_lib_path(RawFile) ++ Includes; process_attr(behaviour, Form, Includes) -> [FileNode] = erl_syntax:attribute_arguments(Form), File = module_to_erl(erl_syntax:atom_value(FileNode)), [File|Includes]; process_attr(compile, Form, Includes) -> [Arg] = erl_syntax:attribute_arguments(Form), case erl_syntax:concrete(Arg) of {parse_transform, Mod} -> [module_to_erl(Mod)|Includes]; {core_transform, Mod} -> [module_to_erl(Mod)|Includes]; L when is_list(L) -> lists:foldl( fun({parse_transform, Mod}, Acc) -> [module_to_erl(Mod)|Acc]; ({core_transform, Mod}, Acc) -> [module_to_erl(Mod)|Acc]; (_, Acc) -> Acc end, Includes, L); _ -> Includes end; process_attr(_, _Form, Includes) -> Includes. module_to_erl(Mod) -> atom_to_list(Mod) ++ ".erl". %% Given the filename from an include_lib attribute, if the path %% exists, return unmodified, or else get the absolute ERL_LIBS %% path. maybe_expand_include_lib_path(File) -> case filelib:is_regular(File) of true -> [File]; false -> expand_include_lib_path(File) end. %% Given a path like "stdlib/include/erl_compile.hrl", return %% "OTP_INSTALL_DIR/lib/erlang/lib/stdlib-x.y.z/include/erl_compile.hrl". %% Usually a simple [Lib, SubDir, File1] = filename:split(File) should %% work, but to not crash when an unusual include_lib path is used, %% utilize more elaborate logic. expand_include_lib_path(File) -> File1 = filename:basename(File), [Lib | Parts] = filename:split(filename:dirname(File)), SubDir = case Parts of [] -> % prevent function clause error []; _ -> filename:join(Parts) end, case code:lib_dir(list_to_atom(Lib), list_to_atom(SubDir)) of {error, bad_name} -> []; Dir -> [filename:join(Dir, File1)] end. %% %% Ensure all files in a list are present and abort if one is missing %% -spec check_files([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.6.0/src/rebar_erlydtl_compiler.erl000066400000000000000000000245621254103774400206510ustar00rootroot00000000000000%% -*- 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'. It can be a list of name-value tuples or a list of %% lists of name-value tuples if you have multiple template directories %% that need to have different settings (see example below). %% %% 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 %% %% recursive: boolean that determines if doc_root(s) need to be %% scanned recursively for matching template file names %% (default: true). %% 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"} %% ]}. %% %% The following example will compile the following templates: %% "src/*.dtl" files into "ebin/*_dtl.beam" and %% "templates/*.html" into "ebin/*.beam". Note that any tuple option %% (such as 'out_dir') in the outer list is added to each inner list: %% {erlydtl_opts, [ %% {out_dir, "ebin"}, %% {recursive, false}, %% [ %% {doc_root, "src"}, {module_ext, "_dtl"} %% ], %% [ %% {doc_root, "templates"}, {module_ext, ""}, {source_ext, ".html"} %% ] %% ]}. -module(rebar_erlydtl_compiler). -export([compile/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== compile(Config, _AppFile) -> MultiDtlOpts = erlydtl_opts(Config), OrigPath = code:get_path(), true = code:add_path(rebar_utils:ebin_dir()), Result = lists:foldl(fun(DtlOpts, _) -> rebar_base_compiler:run(Config, [], option(doc_root, DtlOpts), option(source_ext, DtlOpts), option(out_dir, DtlOpts), option(module_ext, DtlOpts) ++ ".beam", fun(S, T, C) -> compile_dtl(C, S, T, DtlOpts) end, [{check_last_mod, false}, {recursive, option(recursive, DtlOpts)}]) end, ok, MultiDtlOpts), true = rebar_utils:cleanup_code_path(OrigPath), Result. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> ?CONSOLE( "Build ErlyDtl (*.dtl) sources.~n" "~n" "Valid rebar.config options:~n" " ~p~n", [ {erlydtl_opts, [{doc_root, "templates"}, {out_dir, "ebin"}, {source_ext, ".dtl"}, {module_ext, "_dtl"}, {recursive, true}]} ]). erlydtl_opts(Config) -> Opts = rebar_config:get(Config, erlydtl_opts, []), Tuples = [{K,V} || {K,V} <- Opts], case [L || L <- Opts, is_list(L), not io_lib:printable_list(L)] of [] -> [lists:keysort(1, Tuples)]; Lists -> lists:map( fun(L) -> lists:keysort(1, lists:foldl( fun({K,T}, Acc) -> lists:keystore(K, 1, Acc, {K, T}) end, Tuples, L)) end, Lists) end. 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) -> ""; default(compiler_options) -> [return]; default(recursive) -> true. compile_dtl(Config, Source, Target, DtlOpts) -> 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" " https://github.com/erlydtl/erlydtl/releases~n" " and install it into your erlang library dir~n" "===============================================~n~n", []), ?FAIL; _ -> case needs_compile(Source, Target, DtlOpts) of true -> do_compile(Config, Source, Target, DtlOpts); false -> skipped end end. do_compile(Config, Source, Target, DtlOpts) -> %% TODO: Check last mod on target and referenced DTLs here.. %% erlydtl >= 0.8.1 does not use the extra indirection using the %% compiler_options. Kept for backward compatibility with older %% versions of erlydtl. CompilerOptions = option(compiler_options, DtlOpts), Sorted = proplists:unfold( lists:sort( [{out_dir, option(out_dir, DtlOpts)}, {doc_root, option(doc_root, DtlOpts)}, {custom_tags_dir, option(custom_tags_dir, DtlOpts)}, {compiler_options, CompilerOptions} |CompilerOptions])), %% ensure that doc_root and out_dir are defined, %% using defaults if necessary Opts = lists:ukeymerge(1, DtlOpts, Sorted), ?INFO("Compiling \"~s\" -> \"~s\" with options:~n ~s~n", [Source, Target, io_lib:format("~p", [Opts])]), case erlydtl:compile(Source, module_name(Target), Opts) of ok -> ok; {ok, _Mod} -> ok; {ok, _Mod, Ws} -> rebar_base_compiler:ok_tuple(Config, Source, Ws); {ok, _Mod, _Bin, Ws} -> rebar_base_compiler:ok_tuple(Config, Source, Ws); error -> rebar_base_compiler:error_tuple(Config, Source, [], [], Opts); {error, {_File, _Msgs} = Error} -> rebar_base_compiler:error_tuple(Config, Source, [Error], [], Opts); {error, Msg} -> Es = [{Source, [{erlydtl_parser, Msg}]}], rebar_base_compiler:error_tuple(Config, Source, Es, [], Opts); {error, Es, Ws} -> rebar_base_compiler:error_tuple(Config, Source, Es, Ws, Opts) end. module_name(Target) -> F = filename:basename(Target), string:substr(F, 1, length(F)-length(".beam")). needs_compile(Source, Target, DtlOpts) -> LM = filelib:last_modified(Target), LM < filelib:last_modified(Source) orelse lists:any(fun(D) -> LM < filelib:last_modified(D) end, referenced_dtls(Source, DtlOpts)). referenced_dtls(Source, DtlOpts) -> DtlOpts1 = lists:keyreplace(doc_root, 1, DtlOpts, {doc_root, filename:dirname(Source)}), Set = referenced_dtls1([Source], DtlOpts1, sets:add_element(Source, sets:new())), sets:to_list(sets:del_element(Source, Set)). referenced_dtls1(Step, DtlOpts, Seen) -> 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 ], ?DEBUG("All deps: ~p\n", [WithPaths]), 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), DtlOpts, sets:union(New, Seen)) end. rebar-2.6.0/src/rebar_escripter.erl000066400000000000000000000163101254103774400172700ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). %% =================================================================== %% Public API %% =================================================================== escriptize(Config0, AppFile) -> %% Extract the application name from the archive -- this is the default %% name of the generated script {Config, AppName} = rebar_app_utils:app_name(Config0, AppFile), AppNameStr = atom_to_list(AppName), %% 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, []), []), %% Look for a list of extra files to include in the output file. %% For internal rebar-private use only. Do not use outside rebar. InclExtra = get_extra(Config), %% Construct the archive of everything in ebin/ dir -- put it on the %% top-level of the zip file so that code loading works properly. EbinPrefix = filename:join(AppNameStr, "ebin"), EbinFiles = usort(load_files(EbinPrefix, "*", "ebin")), ExtraFiles = usort(InclBeams ++ InclExtra), Files = EbinFiles ++ ExtraFiles, 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"), DefaultEmuArgs = ?FMT("%%! -pa ~s/~s/ebin\n", [AppNameStr, AppNameStr]), EmuArgs = rebar_config:get(Config, escript_emu_args, DefaultEmuArgs), 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#00111), {ok, Config}. clean(Config0, AppFile) -> %% Extract the application name from the archive -- this is the default %% name of the generated script {Config, AppName} = rebar_app_utils:app_name(Config0, 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]), {ok, Config}. %% =================================================================== %% Internal functions %% =================================================================== info(help, escriptize) -> info_help("Generate escript archive"); info(help, clean) -> info_help("Delete generated escript archive"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n", [ Description, {escript_name, "application"}, {escript_incl_apps, []}, {escript_shebang, "#!/usr/bin/env escript\n"}, {escript_comment, "%%\n"}, {escript_emu_args, "%%! -pa application/application/ebin\n"} ]). 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 -> Prefix = filename:join(atom_to_list(App), "ebin"), Acc2 = load_files(Prefix, "*", Path), get_app_beams(Rest, Acc2 ++ Acc) end. get_extra(Config) -> Extra = rebar_config:get_local(Config, escript_incl_extra, []), lists:foldl(fun({Wildcard, Dir}, Files) -> load_files(Wildcard, Dir) ++ Files end, [], Extra). load_files(Wildcard, Dir) -> load_files("", Wildcard, Dir). load_files(Prefix, Wildcard, Dir) -> [read_file(Prefix, Filename, Dir) || Filename <- filelib:wildcard(Wildcard, Dir)]. read_file(Prefix, Filename, Dir) -> Filename1 = case Prefix of "" -> Filename; _ -> filename:join([Prefix, Filename]) end, [dir_entries(filename:dirname(Filename1)), {Filename1, file_contents(filename:join(Dir, Filename))}]. file_contents(Filename) -> {ok, Bin} = file:read_file(Filename), Bin. %% Given a filename, return zip archive dir entries for each sub-dir. %% Required to work around issues fixed in OTP-10071. dir_entries(File) -> Dirs = dirs(File), [{Dir ++ "/", <<>>} || Dir <- Dirs]. %% Given "foo/bar/baz", return ["foo", "foo/bar", "foo/bar/baz"]. dirs(Dir) -> dirs1(filename:split(Dir), "", []). dirs1([], _, Acc) -> lists:reverse(Acc); dirs1([H|T], "", []) -> dirs1(T, H, [H]); dirs1([H|T], Last, Acc) -> Dir = filename:join(Last, H), dirs1(T, Dir, [Dir|Acc]). usort(List) -> lists:ukeysort(1, lists:flatten(List)). rebar-2.6.0/src/rebar_eunit.erl000066400000000000000000000623271254103774400164250ustar00rootroot00000000000000%% -*- 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_DIR 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
  • %%
  • %% suites="foo,bar" - runs tests in foo.erl, test/foo_tests.erl and %% tests in bar.erl, test/bar_tests.erl %%
  • %%
  • %% suites="foo,bar" tests="baz"- runs first test with name starting %% with 'baz' in foo.erl, test/foo_tests.erl and tests in bar.erl, %% test/bar_tests.erl %%
  • %%
  • %% tests="baz"- For every existing suite, run the first test whose %% name starts with bar and, if no such test exists, run the test %% whose name starts with bar in the suite's _tests module %%
  • %%
%% 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, ["src", "dir"]}]}.. %% @copyright 2009, 2010 Dave Smith %% ------------------------------------------------------------------- -module(rebar_eunit). -export([eunit/2, clean/2]). %% for internal use only -export([info/2]). -dialyzer({no_missing_calls, pre15b02_eunit_primitive/3}). -include("rebar.hrl"). -define(EUNIT_DIR, ".eunit"). %% =================================================================== %% Public API %% =================================================================== eunit(Config, _AppFile) -> ok = ensure_dirs(), %% Save code path CodePath = setup_code_path(), CompileOnly = rebar_config:get_global(Config, compile_only, false), {ok, SrcErls} = rebar_erlc_compiler:test_compile(Config, "eunit", ?EUNIT_DIR), case CompileOnly of "true" -> true = rebar_utils:cleanup_code_path(CodePath), ?CONSOLE("Compiled modules for eunit~n", []); false -> run_eunit(Config, CodePath, SrcErls) end. clean(_Config, _File) -> rebar_file_utils:rm_rf(?EUNIT_DIR). %% =================================================================== %% Internal functions %% =================================================================== info(help, eunit) -> info_help("Run eunit tests"); info(help, clean) -> Description = ?FMT("Delete eunit test dir (~s)", [?EUNIT_DIR]), info_help(Description). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" "Valid command line options:~n" " suite[s]=\"foo,bar\" (Run tests in foo.erl, test/foo_tests.erl and~n" " tests in bar.erl, test/bar_tests.erl)~n" " test[s]=\"baz\" (For every existing suite, run the first test whose~n" " name starts with bar and, if no such test exists,~n" " run the test whose name starts with bar in the~n" " suite's _tests module)~n" " random_suite_order=true (Run tests in random order)~n" " random_suite_order=Seed (Run tests in random order,~n" " with the PRNG seeded with Seed)~n" " compile_only=true (Compile but do not run tests)", [ Description, {eunit_opts, []}, {eunit_compile_opts, []}, {eunit_first_files, []}, {cover_enabled, false}, {cover_print_enabled, false}, {cover_export_enabled, false} ]). run_eunit(Config, CodePath, SrcErls) -> %% 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. 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 = randomize_suites(Config, BeamFiles ++ OtherBeamFiles), %% Get matching tests and modules AllModules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- AllBeamFiles], {Tests, FilteredModules} = get_tests_and_modules(Config, ModuleBeamFiles, AllModules), SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls], {ok, CoverLog} = rebar_cover_utils:init(Config, ModuleBeamFiles, eunit_dir()), StatusBefore = status_before_eunit(), EunitResult = perform_eunit(Config, Tests), rebar_cover_utils:perform_cover(Config, FilteredModules, SrcModules, eunit_dir()), rebar_cover_utils:close(CoverLog), case proplists:get_value(reset_after_eunit, get_eunit_opts(Config), true) of true -> reset_after_eunit(StatusBefore); false -> ok end, %% Stop cover to clean the cover_server state. This is important if we want %% eunit+cover to not slow down when analyzing many Erlang modules. ok = rebar_cover_utils:exit(), case EunitResult of ok -> ok; _ -> ?ABORT("One or more eunit tests failed.~n", []) end, %% Restore code path true = rebar_utils:cleanup_code_path(CodePath), ok. ensure_dirs() -> %% Make sure ?EUNIT_DIR/ and ebin/ directory exists (append dummy module) ok = filelib:ensure_dir(filename:join(eunit_dir(), "dummy")), ok = filelib:ensure_dir(filename:join(rebar_utils:ebin_dir(), "dummy")). eunit_dir() -> filename:join(rebar_utils:get_cwd(), ?EUNIT_DIR). setup_code_path() -> %% 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(rebar_utils:ebin_dir()), CodePath. %% %% == get matching tests == %% get_tests_and_modules(Config, ModuleBeamFiles, AllModules) -> SelectedSuites = get_selected_suites(Config, AllModules), {Tests, QualifiedTests} = get_qualified_and_unqualified_tests(Config), Modules = get_test_modules(SelectedSuites, Tests, QualifiedTests, ModuleBeamFiles), FilteredModules = get_matching_modules(AllModules, Modules, QualifiedTests), MatchedTests = get_matching_tests(Modules, Tests, QualifiedTests), {MatchedTests, FilteredModules}. %% %% == get suites specified via 'suites' option == %% get_selected_suites(Config, Modules) -> RawSuites = get_suites(Config), Suites = [list_to_atom(Suite) || Suite <- string:tokens(RawSuites, ",")], [M || M <- Suites, lists:member(M, Modules)]. get_suites(Config) -> case rebar_config:get_global(Config, suites, "") of "" -> rebar_config:get_global(Config, suite, ""); Suites -> Suites end. get_qualified_and_unqualified_tests(Config) -> RawFunctions = rebar_config:get_global(Config, tests, ""), FunctionNames = [FunctionName || FunctionName <- string:tokens(RawFunctions, ",")], get_qualified_and_unqualified_tests1(FunctionNames, [], []). get_qualified_and_unqualified_tests1([], Functions, QualifiedFunctions) -> {Functions, QualifiedFunctions}; get_qualified_and_unqualified_tests1([TestName|TestNames], Functions, QualifiedFunctions) -> case string:tokens(TestName, ":") of [TestName] -> Function = list_to_atom(TestName), get_qualified_and_unqualified_tests1( TestNames, [Function|Functions], QualifiedFunctions); [ModuleName, FunctionName] -> M = list_to_atom(ModuleName), F = list_to_atom(FunctionName), get_qualified_and_unqualified_tests1(TestNames, Functions, [{M, F}|QualifiedFunctions]); _ -> ?ABORT("Unsupported test function specification: ~s~n", [TestName]) end. %% Provide modules which are to be searched for tests. %% Several scenarios are possible: %% %% == randomize suites == %% randomize_suites(Config, Modules) -> case rebar_config:get_global(Config, random_suite_order, undefined) of undefined -> Modules; "true" -> Seed = crypto:rand_uniform(1, 65535), randomize_suites1(Modules, Seed); String -> try list_to_integer(String) of Seed -> randomize_suites1(Modules, Seed) catch error:badarg -> ?ERROR("Bad random seed provided: ~p~n", [String]), ?FAIL end end. randomize_suites1(Modules, Seed) -> _ = random:seed(35, Seed, 1337), ?CONSOLE("Randomizing suite order with seed ~b~n", [Seed]), [X||{_,X} <- lists:sort([{random:uniform(), M} || M <- Modules])]. %% %% == get matching tests == %% 1) Specific tests have been provided and/or %% no unqualified tests have been specified and %% there were some qualified tests, then we can search for %% functions in specified suites (or in empty set of suites). %% %% 2) Neither specific suites nor qualified test names have been %% provided use ModuleBeamFiles which filters out "*_tests" %% modules so EUnit won't doubly run them and cover only %% calculates coverage on production code. However, %% keep "*_tests" modules that are not automatically %% included by EUnit. %% %% From 'Primitives' in the EUnit User's Guide %% http://www.erlang.org/doc/apps/eunit/chapter.html %% "In addition, EUnit will also look for another %% module whose name is ModuleName plus the suffix %% _tests, and if it exists, all the tests from that %% module will also be added. (If ModuleName already %% contains the suffix _tests, this is not done.) E.g., %% the specification {module, mymodule} will run all %% tests in the modules mymodule and mymodule_tests. %% Typically, the _tests module should only contain %% test cases that use the public interface of the main %% module (and no other code)." get_test_modules(SelectedSuites, Tests, QualifiedTests, ModuleBeamFiles) -> SuitesProvided = SelectedSuites =/= [], OnlyQualifiedTestsProvided = QualifiedTests =/= [] andalso Tests =:= [], if SuitesProvided orelse OnlyQualifiedTestsProvided -> SelectedSuites; true -> [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- ModuleBeamFiles] end. get_matching_modules(AllModules, Modules, QualifiedTests) -> ModuleFilterMapper = fun({M, _}) -> case lists:member(M, AllModules) of true -> {true, M}; _-> false end end, ModulesFromQualifiedTests = lists:zf(ModuleFilterMapper, QualifiedTests), lists:usort(Modules ++ ModulesFromQualifiedTests). get_matching_tests(Modules, [], []) -> Modules; get_matching_tests(Modules, [], QualifiedTests) -> FilteredQualifiedTests = filter_qualified_tests(Modules, QualifiedTests), lists:merge(Modules, make_test_primitives(FilteredQualifiedTests)); get_matching_tests(Modules, Tests, QualifiedTests) -> AllTests = lists:merge(QualifiedTests, get_matching_tests1(Modules, Tests, [])), make_test_primitives(AllTests). filter_qualified_tests(Modules, QualifiedTests) -> TestsFilter = fun({Module, _Function}) -> lists:all(fun(M) -> M =/= Module end, Modules) end, lists:filter(TestsFilter, QualifiedTests). get_matching_tests1([], _Functions, TestFunctions) -> TestFunctions; get_matching_tests1([Module|TModules], Functions, TestFunctions) -> %% Get module exports ModuleStr = atom_to_list(Module), ModuleExports = get_beam_test_exports(ModuleStr), %% Get module _tests exports TestModuleStr = string:concat(ModuleStr, "_tests"), TestModuleExports = get_beam_test_exports(TestModuleStr), %% Build tests {M, F} list Tests = get_matching_tests2(Functions, {Module, ModuleExports}, {list_to_atom(TestModuleStr), TestModuleExports}), get_matching_tests1(TModules, Functions, lists:merge([TestFunctions, Tests])). get_matching_tests2(Functions, {Mod, ModExports}, {TestMod, TestModExports}) -> %% Look for matching functions into ModExports ModExportsStr = [atom_to_list(E1) || E1 <- ModExports], TestModExportsStr = [atom_to_list(E2) || E2 <- TestModExports], get_matching_exports(Functions, {Mod, ModExportsStr}, {TestMod, TestModExportsStr}, []). get_matching_exports([], _, _, Matched) -> Matched; get_matching_exports([Function|TFunctions], {Mod, ModExportsStr}, {TestMod, TestModExportsStr}, Matched) -> FunctionStr = atom_to_list(Function), %% Get matching Function in module, otherwise look in _tests module NewMatch = case get_matching_export(FunctionStr, ModExportsStr) of [] -> {TestMod, get_matching_export(FunctionStr, TestModExportsStr)}; MatchingExport -> {Mod, MatchingExport} end, case NewMatch of {_, []} -> get_matching_exports(TFunctions, {Mod, ModExportsStr}, {TestMod, TestModExportsStr}, Matched); _ -> get_matching_exports(TFunctions, {Mod, ModExportsStr}, {TestMod, TestModExportsStr}, [NewMatch|Matched]) end. get_matching_export(_FunctionStr, []) -> []; get_matching_export(FunctionStr, [ExportStr|TExportsStr]) -> case string:str(ExportStr, FunctionStr) of 1 -> list_to_atom(ExportStr); _ -> get_matching_export(FunctionStr, TExportsStr) end. get_beam_test_exports(ModuleStr) -> FilePath = filename:join(eunit_dir(), string:concat(ModuleStr, ".beam")), case filelib:is_regular(FilePath) of true -> {beam_file, _, Exports0, _, _, _} = beam_disasm:file(FilePath), Exports1 = [FunName || {FunName, FunArity, _} <- Exports0, FunArity =:= 0], F = fun(FName) -> FNameStr = atom_to_list(FName), re:run(FNameStr, "_test(_)?") =/= nomatch end, lists:filter(F, Exports1); _ -> [] end. make_test_primitives(RawTests) -> %% Use {test,M,F} and {generator,M,F} if at least R15B02. Otherwise, %% use eunit_test:function_wrapper/2 fallback. %% eunit_test:function_wrapper/2 was renamed to eunit_test:mf_wrapper/2 %% in R15B02; use that as >= R15B02 check. %% TODO: remove fallback and use only {test,M,F} and {generator,M,F} %% primitives once at least R15B02 is required. {module, eunit_test} = code:ensure_loaded(eunit_test), MakePrimitive = case erlang:function_exported(eunit_test, mf_wrapper, 2) of true -> fun eunit_primitive/3; false -> fun pre15b02_eunit_primitive/3 end, ?CONSOLE(" Running test function(s):~n", []), F = fun({M, F2}, Acc) -> ?CONSOLE(" ~p:~p/0~n", [M, F2]), FNameStr = atom_to_list(F2), NewFunction = case re:run(FNameStr, "_test_") of nomatch -> %% Normal test MakePrimitive(test, M, F2); _ -> %% Generator MakePrimitive(generator, M, F2) end, [NewFunction|Acc] end, lists:foldl(F, [], RawTests). eunit_primitive(Type, M, F) -> {Type, M, F}. pre15b02_eunit_primitive(test, M, F) -> eunit_test:function_wrapper(M, F); pre15b02_eunit_primitive(generator, M, F) -> {generator, eunit_test:function_wrapper(M, F)}. %% %% == run tests == %% perform_eunit(Config, Tests) -> 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 = (catch eunit:test(Tests, EunitOpts)), %% Return to original working dir ok = file:set_cwd(Cwd), EunitResult. get_eunit_opts(Config) -> %% Enable verbose in eunit if so requested.. BaseOpts = case rebar_log:is_verbose(Config) of true -> [verbose]; false -> [] end, BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []). %% %% == reset_after_eunit == %% 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)}. get_app_names() -> [AppName || {AppName, _, _} <- application:loaded_applications()]. 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), K =/= included_applications], 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) -> case node() of 'nonode@nohost' -> ?DEBUG("Stopped net kernel.\n", []), ok; _ -> timer:sleep(100), pause_until_net_kernel_stopped(N - 1) end. rebar-2.6.0/src/rebar_file_utils.erl000066400000000000000000000165101254103774400174310ustar00rootroot00000000000000%% -*- 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, write_file_if_contents_differ/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== %% @doc Remove files and directories. %% Target is a single filename, directoryname or wildcard expression. -spec rm_rf(string()) -> 'ok'. rm_rf(Target) -> case os:type() of {unix, _} -> EscTarget = escape_path(Target), {ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]), [{use_stdout, false}, abort_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(list(string()), file:filename()) -> 'ok'. cp_r([], _Dest) -> ok; cp_r(Sources, Dest) -> case os:type() of {unix, _} -> EscSources = [escape_path(Src) || Src <- Sources], SourceStr = string:join(EscSources, " "), {ok, []} = rebar_utils:sh(?FMT("cp -R ~s \"~s\"", [SourceStr, Dest]), [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> lists:foreach(fun(Src) -> ok = cp_r_win32(Src,Dest) end, Sources), ok end. -spec mv(string(), file:filename()) -> 'ok'. mv(Source, Dest) -> case os:type() of {unix, _} -> EscSource = escape_path(Source), EscDest = escape_path(Dest), {ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]), [{use_stdout, false}, abort_on_error]), ok; {win32, _} -> {ok, R} = rebar_utils:sh( ?FMT("move /y \"~s\" \"~s\" 1> nul", [filename:nativename(Source), filename:nativename(Dest)]), [{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. write_file_if_contents_differ(Filename, Bytes) -> ToWrite = iolist_to_binary(Bytes), case file:read_file(Filename) of {ok, ToWrite} -> ok; {ok, _} -> file:write_file(Filename, ToWrite); {error, _} -> file:write_file(Filename, ToWrite) 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_path(Str) -> re:replace(Str, "([ ()?])", "\\\\&", [global, {return, list}]). rebar-2.6.0/src/rebar_getopt.erl000066400000000000000000001236441254103774400166030ustar00rootroot00000000000000%%%------------------------------------------------------------------- %%% @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(rebar_getopt). -author('juanjo@comellas.org'). -export([parse/2, check/2, parse_and_check/2, format_error/2, usage/2, usage/3, usage/4, tokenize/1]). -export([usage_cmd_line/2]). -define(LINE_LENGTH, 75). -define(MIN_USAGE_COMMAND_LINE_OPTION_LENGTH, 25). %% 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)). -define(IS_WHITESPACE(Char), ((Char) =:= $\s orelse (Char) =:= $\t orelse (Char) =:= $\n orelse (Char) =:= $\r)). %% 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'. %% For internal use -type usage_line() :: {OptionText :: string(), HelpText :: string()}. -type usage_line_with_length() :: {OptionLength :: non_neg_integer(), OptionText :: string(), HelpText :: string()}. -export_type([arg_type/0, arg_value/0, arg_spec/0, simple_option/0, compound_option/0, option/0, option_spec/0]). %% @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. Additionally perform check if all required options (the ones %% without default values) are present. The function is a combination of %% two calls: parse/2 and check/2. -spec parse_and_check([option_spec()], string() | [string()]) -> {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: term()}}. parse_and_check(OptSpecList, CmdLine) when is_list(OptSpecList), is_list(CmdLine) -> case parse(OptSpecList, CmdLine) of {ok, {Opts, _}} = Result -> case check(OptSpecList, Opts) of ok -> Result; Error -> Error end; Error -> Error end. %% @doc Check the parsed command line arguments returning ok if all required %% options (i.e. that don't have defaults) are present, and returning %% error otherwise. -spec check([option_spec()], [option()]) -> ok | {error, {Reason :: atom(), Option :: atom()}}. check(OptSpecList, ParsedOpts) when is_list(OptSpecList), is_list(ParsedOpts) -> try RequiredOpts = [Name || {Name, _, _, Arg, _} <- OptSpecList, not is_tuple(Arg) andalso Arg =/= undefined], lists:foreach(fun (Option) -> case proplists:is_defined(Option, ParsedOpts) of true -> ok; false -> throw({error, {missing_required_option, Option}}) end end, RequiredOpts) catch _:Error -> Error end. %% @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 :: term()}}. parse(OptSpecList, CmdLine) when is_list(CmdLine) -> try Args = if is_integer(hd(CmdLine)) -> tokenize(CmdLine); 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 Format the error code returned by prior call to parse/2 or check/2. -spec format_error([option_spec()], {error, {Reason :: atom(), Data :: term()}} | {Reason :: term(), Data :: term()}) -> string(). format_error(OptSpecList, {error, Reason}) -> format_error(OptSpecList, Reason); format_error(OptSpecList, {missing_required_option, Name}) -> {_Name, Short, Long, _Type, _Help} = lists:keyfind(Name, 1, OptSpecList), lists:flatten(["missing required option: -", [Short], " (", to_string(Long), ")"]); format_error(_OptSpecList, {invalid_option, OptStr}) -> lists:flatten(["invalid option: ", to_string(OptStr)]); format_error(_OptSpecList, {invalid_option_arg, {Name, Arg}}) -> lists:flatten(["option \'", to_string(Name) ++ "\' has invalid argument: ", to_string(Arg)]); format_error(_OptSpecList, {invalid_option_arg, OptStr}) -> lists:flatten(["invalid option argument: ", to_string(OptStr)]); format_error(_OptSpecList, {Reason, Data}) -> lists:flatten([to_string(Reason), " ", to_string(Data)]). %% @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, "~s~n~n~s~n", [usage_cmd_line(ProgramName, 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()], ProgramName :: string(), CmdLineTail :: string(), output_stream() | [{string(), string()}]) -> ok. usage(OptSpecList, ProgramName, CmdLineTail, OutputStream) when is_atom(OutputStream) -> io:format(OutputStream, "~s~n~n~s~n", [usage_cmd_line(ProgramName, 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()], ProgramName :: string(), CmdLineTail :: string(), [{OptionName :: string(), Help :: string()}], output_stream()) -> ok. usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, OutputStream) -> io:format(OutputStream, "~s~n~n~s~n", [usage_cmd_line(ProgramName, OptSpecList, CmdLineTail), usage_options(OptSpecList, OptionsTail)]). -spec usage_cmd_line(ProgramName :: string(), [option_spec()]) -> iolist(). usage_cmd_line(ProgramName, OptSpecList) -> usage_cmd_line(ProgramName, OptSpecList, ""). -spec usage_cmd_line(ProgramName :: string(), [option_spec()], CmdLineTail :: string()) -> iolist(). usage_cmd_line(ProgramName, OptSpecList, CmdLineTail) -> Prefix = "Usage: " ++ ProgramName, PrefixLength = length(Prefix), LineLength = line_length(), %% Only align the command line options after the program name when there is %% enough room to do so (i.e. at least 25 characters). If not, show the %% command line options below the program name with a 2-character indentation. if (LineLength - PrefixLength) > ?MIN_USAGE_COMMAND_LINE_OPTION_LENGTH -> Indentation = lists:duplicate(PrefixLength, $\s), [FirstOptLine | OptLines] = usage_cmd_line_options(LineLength - PrefixLength, OptSpecList, CmdLineTail), IndentedOptLines = [[Indentation | OptLine] || OptLine <- OptLines], [Prefix, FirstOptLine | IndentedOptLines]; true -> IndentedOptLines = [[" " | OptLine] || OptLine <- usage_cmd_line_options(LineLength, OptSpecList, CmdLineTail)], [Prefix, $\n, IndentedOptLines] end. %% @doc Return a list of the lines corresponding to the usage command line %% already wrapped according to the maximum MaxLineLength. -spec usage_cmd_line_options(MaxLineLength :: non_neg_integer(), [option_spec()], CmdLineTail :: string()) -> iolist(). usage_cmd_line_options(MaxLineLength, OptSpecList, CmdLineTail) -> usage_cmd_line_options(MaxLineLength, OptSpecList ++ string:tokens(CmdLineTail, " "), [], 0, []). usage_cmd_line_options(MaxLineLength, [OptSpec | Tail], LineAcc, LineAccLength, Acc) -> Option = [$\s | lists:flatten(usage_cmd_line_option(OptSpec))], OptionLength = length(Option), %% We accumulate the options in LineAcc until its length is over the %% maximum allowed line length. When that happens, we append the line in %% LineAcc to the list with all the lines in the command line (Acc). NewLineAccLength = LineAccLength + OptionLength, if NewLineAccLength < MaxLineLength -> usage_cmd_line_options(MaxLineLength, Tail, [Option | LineAcc], NewLineAccLength, Acc); true -> usage_cmd_line_options(MaxLineLength, Tail, [Option], OptionLength + 1, [lists:reverse([$\n | LineAcc]) | Acc]) end; usage_cmd_line_options(MaxLineLength, [], [_ | _] = LineAcc, _LineAccLength, Acc) -> %% If there was a non-empty line in LineAcc when there are no more options %% to process, we add it to the list of lines to return. usage_cmd_line_options(MaxLineLength, [], [], 0, [lists:reverse(LineAcc) | Acc]); usage_cmd_line_options(_MaxLineLength, [], [], _LineAccLength, Acc) -> lists:reverse(Acc). -spec usage_cmd_line_option(option_spec()) -> string(). usage_cmd_line_option({_Name, Short, _Long, undefined, _Help}) when Short =/= undefined -> %% For options with short form and no argument. [$[, $-, Short, $]]; usage_cmd_line_option({_Name, _Short, Long, undefined, _Help}) when Long =/= undefined -> %% For options with only long form and no argument. [$[, $-, $-, Long, $]]; usage_cmd_line_option({_Name, _Short, _Long, undefined, _Help}) -> []; usage_cmd_line_option({Name, Short, Long, ArgSpec, _Help}) when is_atom(ArgSpec) -> %% For options with no default argument. if %% For options with short form and argument. Short =/= undefined -> [$[, $-, Short, $\s, $<, atom_to_list(Name), $>, $]]; %% For options with only long form and argument. Long =/= undefined -> [$[, $-, $-, Long, $\s, $<, atom_to_list(Name), $>, $]]; %% For options with neither short nor long form and argument. true -> [$[, $<, atom_to_list(Name), $>, $]] end; usage_cmd_line_option({Name, Short, Long, ArgSpec, _Help}) when is_tuple(ArgSpec) -> %% For options with default argument. if %% For options with short form and default argument. Short =/= undefined -> [$[, $-, Short, $\s, $[, $<, atom_to_list(Name), $>, $], $]]; %% For options with only long form and default argument. Long =/= undefined -> [$[, $-, $-, Long, $\s, $[, $<, atom_to_list(Name), $>, $], $]]; %% For options with neither short nor long form and default argument. true -> [$[, $<, atom_to_list(Name), $>, $]] end; usage_cmd_line_option(Option) when is_list(Option) -> %% For custom options that are added to the command line. Option. %% @doc Return a list of help messages to print for each of the options and arguments. -spec usage_options([option_spec()]) -> [string()]. usage_options(OptSpecList) -> usage_options(OptSpecList, []). %% @doc Return a list of usage lines to print for each of the options and arguments. -spec usage_options([option_spec()], [{OptionName :: string(), Help :: string()}]) -> [string()]. usage_options(OptSpecList, CustomHelp) -> %% Add the usage lines corresponding to the option specifications. {MaxOptionLength0, UsageLines0} = add_option_spec_help_lines(OptSpecList, 0, []), %% Add the custom usage lines. {MaxOptionLength, UsageLines} = add_custom_help_lines(CustomHelp, MaxOptionLength0, UsageLines0), MaxLineLength = line_length(), lists:reverse([format_usage_line(MaxOptionLength + 1, MaxLineLength, UsageLine) || UsageLine <- UsageLines]). -spec add_option_spec_help_lines([option_spec()], PrevMaxOptionLength :: non_neg_integer(), [usage_line_with_length()]) -> {MaxOptionLength :: non_neg_integer(), [usage_line_with_length()]}. add_option_spec_help_lines([OptSpec | Tail], PrevMaxOptionLength, Acc) -> OptionText = usage_option_text(OptSpec), HelpText = usage_help_text(OptSpec), {MaxOptionLength, ColsWithLength} = get_max_option_length({OptionText, HelpText}, PrevMaxOptionLength), add_option_spec_help_lines(Tail, MaxOptionLength, [ColsWithLength | Acc]); add_option_spec_help_lines([], MaxOptionLength, Acc) -> {MaxOptionLength, Acc}. -spec add_custom_help_lines([usage_line()], PrevMaxOptionLength :: non_neg_integer(), [usage_line_with_length()]) -> {MaxOptionLength :: non_neg_integer(), [usage_line_with_length()]}. add_custom_help_lines([CustomCols | Tail], PrevMaxOptionLength, Acc) -> {MaxOptionLength, ColsWithLength} = get_max_option_length(CustomCols, PrevMaxOptionLength), add_custom_help_lines(Tail, MaxOptionLength, [ColsWithLength | Acc]); add_custom_help_lines([], MaxOptionLength, Acc) -> {MaxOptionLength, Acc}. -spec usage_option_text(option_spec()) -> string(). usage_option_text({Name, undefined, undefined, _ArgSpec, _Help}) -> %% Neither short nor long form (non-option argument). "<" ++ atom_to_list(Name) ++ ">"; usage_option_text({_Name, Short, undefined, _ArgSpec, _Help}) -> %% Only short form. [$-, Short]; usage_option_text({_Name, undefined, Long, _ArgSpec, _Help}) -> %% Only long form. [$-, $- | Long]; usage_option_text({_Name, Short, Long, _ArgSpec, _Help}) -> %% Both short and long form. [$-, Short, $,, $\s, $-, $- | Long]. -spec usage_help_text(option_spec()) -> string(). usage_help_text({_Name, _Short, _Long, {_ArgType, ArgValue}, [_ | _] = Help}) -> Help ++ " [default: " ++ default_arg_value_to_string(ArgValue) ++ "]"; usage_help_text({_Name, _Short, _Long, _ArgSpec, Help}) -> Help. %% @doc Calculate the maximum width of the column that shows the option's short %% and long form. -spec get_max_option_length(usage_line(), PrevMaxOptionLength :: non_neg_integer()) -> {MaxOptionLength :: non_neg_integer(), usage_line_with_length()}. get_max_option_length({OptionText, HelpText}, PrevMaxOptionLength) -> OptionLength = length(OptionText), {erlang:max(OptionLength, PrevMaxOptionLength), {OptionLength, OptionText, HelpText}}. %% @doc Format the usage line that is shown for the options' usage. Each usage %% line has 2 columns. The first column shows the options in their short %% and long form. The second column shows the wrapped (if necessary) help %% text lines associated with each option. e.g.: %% %% -h, --host Database server host name or IP address; this is the %% hostname of the server where the database is running %% [default: localhost] %% -p, --port Database server port [default: 1000] %% -spec format_usage_line(MaxOptionLength :: non_neg_integer(), MaxLineLength :: non_neg_integer(), usage_line_with_length()) -> iolist(). format_usage_line(MaxOptionLength, MaxLineLength, {OptionLength, OptionText, [_ | _] = HelpText}) when MaxOptionLength < (MaxLineLength div 2) -> %% If the width of the column where the options are shown is smaller than %% half the width of a console line then we show the help text line aligned %% next to its corresponding option, with a separation of at least 2 %% characters. [Head | Tail] = wrap_text_line(MaxLineLength - MaxOptionLength - 3, HelpText), FirstLineIndentation = lists:duplicate(MaxOptionLength - OptionLength + 1, $\s), Indentation = [$\n | lists:duplicate(MaxOptionLength + 3, $\s)], [" ", OptionText, FirstLineIndentation, Head, [[Indentation, Line] || Line <- Tail], $\n]; format_usage_line(_MaxOptionLength, MaxLineLength, {_OptionLength, OptionText, [_ | _] = HelpText}) -> %% If the width of the first column is bigger than the width of a console %% line, we show the help text on the next line with an indentation of 6 %% characters. HelpLines = wrap_text_line(MaxLineLength - 6, HelpText), [" ", OptionText, [["\n ", Line] || Line <- HelpLines], $\n]; format_usage_line(_MaxOptionLength, _MaxLineLength, {_OptionLength, OptionText, _HelpText}) -> [" ", OptionText, $\n]. %% @doc Wrap a text line converting it into several text lines so that the %% length of each one of them is never over Length characters. -spec wrap_text_line(Length :: non_neg_integer(), Text :: string()) -> [string()]. wrap_text_line(Length, Text) -> wrap_text_line(Length, Text, [], 0, []). wrap_text_line(Length, [Char | Tail], Acc, Count, CurrentLineAcc) when Count < Length -> wrap_text_line(Length, Tail, Acc, Count + 1, [Char | CurrentLineAcc]); wrap_text_line(Length, [_ | _] = Help, Acc, Count, CurrentLineAcc) -> %% Look for the first whitespace character in the current (reversed) line %% buffer to get a wrapped line. If there is no whitespace just cut the %% line at the position corresponding to the maximum length. {NextLineAcc, WrappedLine} = case string:cspan(CurrentLineAcc, " \t") of WhitespacePos when WhitespacePos < Count -> lists:split(WhitespacePos, CurrentLineAcc); _ -> {[], CurrentLineAcc} end, wrap_text_line(Length, Help, [lists:reverse(WrappedLine) | Acc], length(NextLineAcc), NextLineAcc); wrap_text_line(_Length, [], Acc, _Count, [_ | _] = CurrentLineAcc) -> %% If there was a non-empty line when we reached the buffer, add it to the accumulator lists:reverse([lists:reverse(CurrentLineAcc) | Acc]); wrap_text_line(_Length, [], Acc, _Count, _CurrentLineAcc) -> lists:reverse(Acc). default_arg_value_to_string(Value) when is_atom(Value) -> atom_to_list(Value); default_arg_value_to_string(Value) when is_binary(Value) -> binary_to_list(Value); default_arg_value_to_string(Value) when is_integer(Value) -> integer_to_list(Value); default_arg_value_to_string(Value) when is_float(Value) -> lists:flatten(io_lib:format("~w", [Value])); default_arg_value_to_string(Value) -> Value. %% @doc Tokenize a command line string with support for single and double %% quoted arguments (needed for arguments that have embedded whitespace). %% The function also supports the expansion of environment variables in %% both the Unix (${VAR}; $VAR) and Windows (%VAR%) formats. It does NOT %% support wildcard expansion of paths. -spec tokenize(CmdLine :: string()) -> [nonempty_string()]. tokenize(CmdLine) -> tokenize(CmdLine, [], []). -spec tokenize(CmdLine :: string(), Acc :: [string()], ArgAcc :: string()) -> [string()]. tokenize([Sep | Tail], Acc, ArgAcc) when ?IS_WHITESPACE(Sep) -> NewAcc = case ArgAcc of [_ | _] -> %% Found separator: add to the list of arguments. [lists:reverse(ArgAcc) | Acc]; [] -> %% Found separator with no accumulated argument; discard it. Acc end, tokenize(Tail, NewAcc, []); tokenize([QuotationMark | Tail], Acc, ArgAcc) when QuotationMark =:= $"; QuotationMark =:= $' -> %% Quoted argument (might contain spaces, tabs, etc.) tokenize_quoted_arg(QuotationMark, Tail, Acc, ArgAcc); tokenize([Char | _Tail] = CmdLine, Acc, ArgAcc) when Char =:= $$; Char =:= $% -> %% Unix and Windows environment variable expansion: ${VAR}; $VAR; %VAR% {NewCmdLine, Var} = expand_env_var(CmdLine), tokenize(NewCmdLine, Acc, lists:reverse(Var, ArgAcc)); tokenize([$\\, Char | Tail], Acc, ArgAcc) -> %% Escaped char. tokenize(Tail, Acc, [Char | ArgAcc]); tokenize([Char | Tail], Acc, ArgAcc) -> tokenize(Tail, Acc, [Char | ArgAcc]); tokenize([], Acc, []) -> lists:reverse(Acc); tokenize([], Acc, ArgAcc) -> lists:reverse([lists:reverse(ArgAcc) | Acc]). -spec tokenize_quoted_arg(QuotationMark :: char(), CmdLine :: string(), Acc :: [string()], ArgAcc :: string()) -> [string()]. tokenize_quoted_arg(QuotationMark, [QuotationMark | Tail], Acc, ArgAcc) -> %% End of quoted argument tokenize(Tail, Acc, ArgAcc); tokenize_quoted_arg(QuotationMark, [$\\, Char | Tail], Acc, ArgAcc) -> %% Escaped char. tokenize_quoted_arg(QuotationMark, Tail, Acc, [Char | ArgAcc]); tokenize_quoted_arg($" = QuotationMark, [Char | _Tail] = CmdLine, Acc, ArgAcc) when Char =:= $$; Char =:= $% -> %% Unix and Windows environment variable expansion (only for double-quoted arguments): ${VAR}; $VAR; %VAR% {NewCmdLine, Var} = expand_env_var(CmdLine), tokenize_quoted_arg(QuotationMark, NewCmdLine, Acc, lists:reverse(Var, ArgAcc)); tokenize_quoted_arg(QuotationMark, [Char | Tail], Acc, ArgAcc) -> tokenize_quoted_arg(QuotationMark, Tail, Acc, [Char | ArgAcc]); tokenize_quoted_arg(_QuotationMark, CmdLine, Acc, ArgAcc) -> tokenize(CmdLine, Acc, ArgAcc). -spec expand_env_var(CmdLine :: nonempty_string()) -> {string(), string()}. expand_env_var(CmdLine) -> case CmdLine of "${" ++ Tail -> expand_env_var("${", $}, Tail, []); "$" ++ Tail -> expand_env_var("$", Tail, []); "%" ++ Tail -> expand_env_var("%", $%, Tail, []) end. -spec expand_env_var(Prefix :: string(), EndMark :: char(), CmdLine :: string(), Acc :: string()) -> {string(), string()}. expand_env_var(Prefix, EndMark, [Char | Tail], Acc) when (Char >= $A andalso Char =< $Z) orelse (Char >= $a andalso Char =< $z) orelse (Char >= $0 andalso Char =< $9) orelse (Char =:= $_) -> expand_env_var(Prefix, EndMark, Tail, [Char | Acc]); expand_env_var(Prefix, EndMark, [EndMark | Tail], Acc) -> {Tail, get_env_var(Prefix, [EndMark], Acc)}; expand_env_var(Prefix, _EndMark, CmdLine, Acc) -> {CmdLine, Prefix ++ lists:reverse(Acc)}. -spec expand_env_var(Prefix :: string(), CmdLine :: string(), Acc :: string()) -> {string(), string()}. expand_env_var(Prefix, [Char | Tail], Acc) when (Char >= $A andalso Char =< $Z) orelse (Char >= $a andalso Char =< $z) orelse (Char >= $0 andalso Char =< $9) orelse (Char =:= $_) -> expand_env_var(Prefix, Tail, [Char | Acc]); expand_env_var(Prefix, CmdLine, Acc) -> {CmdLine, get_env_var(Prefix, "", Acc)}. -spec get_env_var(Prefix :: string(), Suffix :: string(), Acc :: string()) -> string(). get_env_var(Prefix, Suffix, [_ | _] = Acc) -> Name = lists:reverse(Acc), %% Only expand valid/existing variables. case os:getenv(Name) of false -> Prefix ++ Name ++ Suffix; Value -> Value end; get_env_var(Prefix, Suffix, []) -> Prefix ++ Suffix. -spec line_length() -> 0..?LINE_LENGTH. line_length() -> case io:columns() of {ok, Columns} when Columns < ?LINE_LENGTH -> Columns - 1; _ -> ?LINE_LENGTH end. -spec to_string(term()) -> string(). to_string(List) when is_list(List) -> case io_lib:printable_list(List) of true -> List; false -> io_lib:format("~p", [List]) end; to_string(Atom) when is_atom(Atom) -> atom_to_list(Atom); to_string(Value) -> io_lib:format("~p", [Value]). rebar-2.6.0/src/rebar_lfe_compiler.erl000066400000000000000000000065361254103774400177410ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/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 %% =================================================================== info(help, compile) -> ?CONSOLE( "Build Lisp Flavoured Erlang (*.lfe) sources.~n" "~n" "Valid rebar.config options:~n" " erl_opts is reused.'~n", []). 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; _ -> ErlOpts = rebar_utils:erl_opts(Config), Opts = [{i, "include"}, {outdir, "ebin"}, return] ++ ErlOpts, case lfe_comp:file(Source, Opts) of {ok, _Mod, Ws} -> rebar_base_compiler:ok_tuple(Config, Source, Ws); {error, Es, Ws} -> rebar_base_compiler:error_tuple(Config, Source, Es, Ws, Opts); _ -> ?FAIL end end. rebar-2.6.0/src/rebar_log.erl000066400000000000000000000064111254103774400160520ustar00rootroot00000000000000%% -*- 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/1, set_level/1, error_level/0, default_level/0, log/3, log/4, is_verbose/1]). -define(ERROR_LEVEL, 0). -define(WARN_LEVEL, 1). -define(INFO_LEVEL, 2). -define(DEBUG_LEVEL, 3). %% =================================================================== %% Public API %% =================================================================== init(Config) -> Verbosity = rebar_config:get_global(Config, verbose, default_level()), case valid_level(Verbosity) of ?ERROR_LEVEL -> set_level(error); ?WARN_LEVEL -> set_level(warn); ?INFO_LEVEL -> set_level(info); ?DEBUG_LEVEL -> set_level(debug) end. set_level(Level) -> ok = application:set_env(rebar, log_level, Level). log(Level, Str, Args) -> log(standard_io, Level, Str, Args). log(Device, Level, Str, Args) -> {ok, LogLevel} = application:get_env(rebar, log_level), case should_log(LogLevel, Level) of true -> io:format(Device, log_prefix(Level) ++ Str, Args); false -> ok end. error_level() -> ?ERROR_LEVEL. default_level() -> ?WARN_LEVEL. is_verbose(Config) -> rebar_config:get_xconf(Config, is_verbose, false). %% =================================================================== %% Internal functions %% =================================================================== valid_level(Level) -> erlang:max(?ERROR_LEVEL, erlang:min(Level, ?DEBUG_LEVEL)). 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.6.0/src/rebar_metacmds.erl000066400000000000000000000044421254103774400170700ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2013-2014 Tuncer Ayaz %% %% 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_metacmds). -export(['prepare-deps'/2, 'refresh-deps'/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== 'prepare-deps'(Config, _AppFile) -> rebar:run(enable_recursion(Config), ["get-deps", "compile"]). 'refresh-deps'(Config, _AppFile) -> rebar:run(enable_recursion(Config), ["update-deps", "compile"]). %% =================================================================== %% Internal functions %% =================================================================== info(help, 'prepare-deps') -> ?CONSOLE("Meta command to run 'rebar -r get-deps compile'.~n", []); info(help, 'refresh-deps') -> ?CONSOLE("Meta command to run 'rebar -r update-deps compile'.~n", []). enable_recursion(Config) -> rebar_config:set_xconf(Config, recursive, true). rebar-2.6.0/src/rebar_mustache.erl000066400000000000000000000170331254103774400171040ustar00rootroot00000000000000%% 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(rebar_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}). -define(MUSTACHE_STR, "rebar_mustache"). 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_STR ++ ":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_STR ++ ":escape(" ++ ?MUSTACHE_STR ++ ":get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ "))"; compile_tag("{", Content, State) -> Mod = State#mstate.mod, ?MUSTACHE_STR ++ ":get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ ")"; compile_tag("!", _Content, _State) -> "[]". template_dir(Mod) -> DefaultDirPath = filename:dirname(code:which(Mod)), case application:get_env(mustache, templates_dir) of {ok, DirPath} when is_list(DirPath) -> case filelib:ensure_dir(DirPath) of ok -> DirPath; _ -> DefaultDirPath end; _ -> DefaultDirPath end. template_path(Mod) -> DirPath = template_dir(Mod), Basename = atom_to_list(Mod), filename:join(DirPath, Basename ++ ".mustache"). 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", []). rebar-2.6.0/src/rebar_neotoma_compiler.erl000066400000000000000000000142111254103774400206220ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/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) ++ ".erl", fun compile_neo/3, [{check_last_mod, true}]). %% ============================================================================ %% Internal functions %% ============================================================================ info(help, compile) -> ?CONSOLE( "Build Neotoma (*.peg) sources.~n" "~n" "Valid rebar.config options:~n" " ~p~n", [ {neotoma_opts, [{doc_root, "src"}, {out_dir, "src"}, {source_ext, ".peg"}, {module_ext, ""}]} ]). 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.6.0/src/rebar_otp_app.erl000066400000000000000000000177751254103774400167520ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/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. {Config1, AppFile} = case rebar_app_utils:is_app_src(File) of true -> preprocess(Config, File); false -> {Config, File} end, %% Load the app file and validate it. case rebar_app_utils:load_app_file(Config1, AppFile) of {ok, Config2, 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(Config1, validate_app_modules, true) of true -> Modules = proplists:get_value(modules, AppData), {validate_modules(AppName, Modules), Config2}; false -> {ok, Config2} 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 %% =================================================================== info(help, compile) -> info_help("Validate .app file"); info(help, clean) -> info_help("Delete .app file if generated from .app.src"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n", [ Description, {validate_app_modules, true} ]). preprocess(Config, AppSrcFile) -> case rebar_app_utils:load_app_file(Config, AppSrcFile) of {ok, Config1, 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(Config1) ++ [{modules, ebin_modules()}], A1 = apply_app_vars(AppVars, AppData), %% AppSrcFile may contain instructions for generating a vsn number {Config2, Vsn} = rebar_app_utils:app_vsn(Config1, AppSrcFile), A2 = lists:keystore(vsn, 1, A1, {vsn, Vsn}), %% systools:make_relup/4 fails with {missing_param, registered} %% without a 'registered' value. A3 = ensure_registered(A2), %% Build the final spec as a string Spec = io_lib:format("~p.\n", [{application, AppName, A3}]), %% Setup file .app filename and write new contents AppFile = rebar_app_utils:app_src_to_app(AppSrcFile), ok = rebar_file_utils:write_file_if_contents_differ(AppFile, Spec), %% Make certain that the ebin/ directory is available %% on the code path true = code:add_path(filename:absname(filename:dirname(AppFile))), {Config2, 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")]). ensure_registered(AppData) -> case lists:keyfind(registered, 1, AppData) of false -> [{registered, []} | AppData]; {registered, _} -> %% We could further check whether the value is a list of atoms. AppData end. rebar-2.6.0/src/rebar_otp_appup.erl000066400000000000000000000072011254103774400172760ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Luis Rascao (luis.rascao@gmail.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_appup). -export([compile/2, clean/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== -spec compile(rebar_config:config(), file:filename()) -> 'ok'. compile(Config, _AppFile) -> %% If we get an *.appup.src file, it needs to be pre-processed and %% written out as a ebin/*.appup file. Files = rebar_utils:find_files_by_ext("src", ".appup.src"), Targets = [filename:join("ebin", filename:rootname(filename:basename(F))) || F <- Files], rebar_base_compiler:run(Config, [], lists:zip(Files, Targets), fun preprocess/3, [{check_last_mod, true}]). clean(Config, _AppFile) -> Files = rebar_utils:find_files_by_ext("ebin", ".appup"), rebar_file_utils:delete_each([Files]), {ok, Config}. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> ?CONSOLE("Validate .appup.src file", []); info(help, clean) -> ?CONSOLE("Delete .appup file if generated from .appup.src", []). preprocess(SourceFile, TargetFile, _Config) -> %% Perform basic validation on the appup file %% i.e. if a consult succeeds and basic appup %% structure exists. case rebar_config:consult_file(SourceFile) of %% The .appup syntax is described in %% http://erlang.org/doc/man/appup.html. {ok, [{_Vsn, UpFromVsn, DownToVsn} = AppUp]} when is_list(UpFromVsn), is_list(DownToVsn) -> case file:write_file( TargetFile, lists:flatten(io_lib:format("~p.", [AppUp]))) of {error, Reason} -> ?ABORT("Failed writing to target file ~s due to ~s", [TargetFile, Reason]); ok -> ok end; {error, Reason} -> ?ABORT("Failed to compile ~s: ~p~n", [SourceFile, Reason]); _ -> ?ABORT("Failed to compile ~s, not an appup~n", [SourceFile]) end. rebar-2.6.0/src/rebar_port_compiler.erl000066400000000000000000000612011254103774400201450ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([setup_env/1, info/2]). -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"}]} %% -record(spec, {type::'drv' | 'exe', target::file:filename(), sources = [] :: [file:filename(), ...], objects = [] :: [file:filename(), ...], opts = [] ::list() | []}). compile(Config, AppFile) -> case get_specs(Config, AppFile) of [] -> ok; Specs -> SharedEnv = rebar_config:get_env(Config, rebar_deps) ++ rebar_config:get_env(Config, ?MODULE), %% Compile each of the sources NewBins = compile_sources(Config, 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) -> case get_specs(Config, AppFile) of [] -> ok; Specs -> lists:foreach(fun(#spec{target=Target, objects=Objects}) -> rebar_file_utils:delete_each([Target]), rebar_file_utils:delete_each(Objects), rebar_file_utils:delete_each(port_deps(Objects)) end, Specs) end, ok. setup_env(Config) -> setup_env(Config, []). %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> info_help("Build port sources"); info(help, clean) -> info_help("Delete port build results"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n", [ Description, {port_env, [{"CFLAGS", "$CFLAGS -Ifoo"}, {"freebsd", "LDFLAGS", "$LDFLAGS -lfoo"}]}, {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, []}]}]} ]). 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(), []), %% Get any port-specific envs; use port_env first and then fallback %% to port_envs for compatibility RawPortEnv = rebar_config:get_list(Config, port_env, rebar_config:get_list(Config, port_envs, [])), PortEnv = filter_env(RawPortEnv, []), Defines = get_defines(Config), OverrideEnv = Defines ++ PortEnv ++ filter_env(ExtraEnv, []), RawEnv = apply_defaults(os_env(), DefaultEnv) ++ OverrideEnv, expand_vars_loop(merge_each_var(RawEnv, [])). get_defines(Config) -> RawDefines = rebar_config:get_xconf(Config, defines, []), Defines = string:join(["-D" ++ D || D <- RawDefines], " "), [{"ERL_CFLAGS", "$ERL_CFLAGS " ++ Defines}]. 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(Config, Specs, SharedEnv) -> lists:foldl( fun(#spec{sources=Sources, type=Type, opts=Opts}, NewBins) -> Env = proplists:get_value(env, Opts, SharedEnv), compile_each(Config, Sources, Type, Env, NewBins) end, [], Specs). compile_each(_Config, [], _Type, _Env, NewBins) -> lists:reverse(NewBins); compile_each(Config, [Source | Rest], Type, Env, NewBins) -> Ext = filename:extension(Source), Bin = replace_extension(Source, Ext, ".o"), case needs_compile(Source, Bin) of true -> Template = select_compile_template(Type, compiler(Ext)), Cmd = expand_command(Template, Env, Source, Bin), ShOpts = [{env, Env}, return_on_error, {use_stdout, false}], exec_compiler(Config, Source, Cmd, ShOpts), compile_each(Config, Rest, Type, Env, [Bin | NewBins]); false -> ?INFO("Skipping ~s\n", [Source]), compile_each(Config, Rest, Type, Env, NewBins) end. exec_compiler(Config, Source, Cmd, ShOpts) -> case rebar_utils:sh(Cmd, ShOpts) of {error, {_RC, RawError}} -> AbsSource = case rebar_utils:processing_base_dir(Config) of true -> Source; false -> filename:absname(Source) end, ?CONSOLE("Compiling ~s\n", [AbsSource]), Error = re:replace(RawError, Source, AbsSource, [{return, list}, global]), ?CONSOLE("~s", [Error]), ?FAIL; {ok, Output} -> ?CONSOLE("Compiling ~s\n", [Source]), ?CONSOLE("~s", [Output]) end. needs_compile(Source, Bin) -> needs_link(Bin, [Source|bin_deps(Bin)]). %% NOTE: This relies on -MMD being passed to the compiler and returns an %% empty list if the .d file is not available. This means header deps are %% ignored on win32. bin_deps(Bin) -> [DepFile] = port_deps([Bin]), case file:read_file(DepFile) of {ok, Deps} -> Ds = parse_bin_deps(list_to_binary(Bin), Deps), ?DEBUG("Deps of ~p: ~p\n", [Bin, Ds]), Ds; {error, Err} -> ?DEBUG("Skipping deps parse of ~s: ~p\n", [DepFile, Err]), [] end. parse_bin_deps(Bin, Deps) -> Sz = size(Bin), <> = Deps, Ds = re:split(X, "\\s*\\\\\\R\\s*|\\s+", [{return, binary}]), [D || D <- Ds, D =/= <<>>]. 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) -> Specs = case rebar_config:get_local(Config, port_specs, []) of [] -> %% No spec provided. Construct a spec %% from old-school so_name and sources [port_spec_from_legacy(Config, AppFile)]; PortSpecs -> Filtered = filter_port_specs(PortSpecs), OsType = os:type(), [get_port_spec(Config, OsType, Spec) || Spec <- Filtered] end, [S || S <- Specs, S#spec.sources /= []]. port_spec_from_legacy(Config, AppFile) -> %% Get the target from the so_name variable Target = case rebar_config:get(Config, so_name, undefined) of undefined -> %% Generate a sensible default from app file {_, AppName} = rebar_app_utils:app_name(Config, AppFile), filename:join("priv", lists:concat([AppName, "_drv.so"])); AName -> %% Old form is available -- use it filename:join("priv", AName) end, %% Get the list of source files from port_sources Sources = port_sources(rebar_config:get_list(Config, port_sources, ["c_src/*.c"])), #spec { type = target_type(Target), target = maybe_switch_extension(os:type(), Target), sources = Sources, objects = port_objects(Sources) }. 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_deps(SourceFiles) -> [replace_extension(O, ".d") || 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. %% %% == 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}, unicode], 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), rebar_utils:expand_env_variable(Cmd1, "PORT_OUT_FILE", OutFile). %% %% Given a string, determine if it is expandable %% is_expandable(InStr) -> case re:run(InStr,"\\\$",[{capture,none}]) of match -> true; nomatch -> false end. %% %% 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() -> ReOpts = [{return, list}, {parts, 2}, unicode], Os = [list_to_tuple(re:split(S, "=", ReOpts)) || S <- lists:filter(fun discard_deps_vars/1, os:getenv())], %% Drop variables without a name (win32) [T1 || {K, _V} = T1 <- Os, K =/= []]. %% %% To avoid having multiple repetitions of the same environment variables %% (ERL_LIBS), avoid exporting any variables that may cause conflict with %% those exported by the rebar_deps module (ERL_LIBS, REBAR_DEPS_DIR) %% discard_deps_vars("ERL_LIBS=" ++ _Value) -> false; discard_deps_vars("REBAR_DEPS_DIR=" ++ _Value) -> false; discard_deps_vars(_Var) -> true. 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 -MMD $ERL_CFLAGS"}, {"DRV_LDFLAGS", "-shared $ERL_LDFLAGS"}, {"EXE_CFLAGS" , "-g -Wall -fPIC -MMD $ERL_CFLAGS"}, {"EXE_LDFLAGS", "$ERL_LDFLAGS"}, {"ERL_CFLAGS", lists:concat([" -I\"", erl_interface_dir(include), "\" -I\"", filename:join(erts_dir(), "include"), "\" "])}, {"ERL_EI_LIBDIR", lists:concat(["\"", 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"}, %% Linux specific flags for multiarch {"linux.*-64$", "CFLAGS", "-m64 $CFLAGS"}, {"linux.*-64$", "CXXFLAGS", "-m64 $CXXFLAGS"}, {"linux.*-64$", "LDFLAGS", "$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, Lion, and Mountain Lion flags for 32-bit {"darwin1[0-2].*-32", "CFLAGS", "-m32 $CFLAGS"}, {"darwin1[0-2].*-32", "CXXFLAGS", "-m32 $CXXFLAGS"}, {"darwin1[0-2].*-32", "LDFLAGS", "-arch i386 $LDFLAGS"}, %% Windows specific flags %% add MS Visual C++ support to rebar on Windows {"win32", "CC", "cl.exe"}, {"win32", "CXX", "cl.exe"}, {"win32", "LINKER", "link.exe"}, {"win32", "DRV_CXX_TEMPLATE", %% DRV_* and EXE_* Templates are identical "$CXX /c $CXXFLAGS $DRV_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"}, {"win32", "DRV_CC_TEMPLATE", "$CC /c $CFLAGS $DRV_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"}, {"win32", "DRV_LINK_TEMPLATE", "$LINKER $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS /OUT:$PORT_OUT_FILE"}, %% DRV_* and EXE_* Templates are identical {"win32", "EXE_CXX_TEMPLATE", "$CXX /c $CXXFLAGS $EXE_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"}, {"win32", "EXE_CC_TEMPLATE", "$CC /c $CFLAGS $EXE_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"}, {"win32", "EXE_LINK_TEMPLATE", "$LINKER $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS /OUT:$PORT_OUT_FILE"}, %% ERL_CFLAGS are ok as -I even though strictly it should be /I {"win32", "ERL_LDFLAGS", " /LIBPATH:$ERL_EI_LIBDIR erl_interface.lib ei.lib"}, {"win32", "DRV_CFLAGS", "/Zi /Wall $ERL_CFLAGS"}, {"win32", "DRV_LDFLAGS", "/DLL $ERL_LDFLAGS"} ]. rebar-2.6.0/src/rebar_proto_compiler.erl000066400000000000000000000136021254103774400203260ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tomas Abrahamsson (tomas.abrahamsson@gmail.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_proto_compiler). -export([compile/2, clean/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). -record(proto_compiler, {key :: atom(), % Corresponds to the {proto_compiler,Key} module :: atom() }). %% =================================================================== %% Public API %% =================================================================== find_proto_files(ProtoDirs) -> lists:foldl(fun(ProtoDir, Acc) -> rebar_utils:find_files_by_ext(ProtoDir, ".proto") ++ Acc end, [], ProtoDirs). compile(Config, AppFile) -> %% Find a compiler for protocol buffers, %% use that for compiling protocol buffers {CompilerModule, ProtoDirs} = select_proto_compiler_and_dir(Config), case find_proto_files(ProtoDirs) of [] -> ok; Protos -> %% Ask the proto compiler to compile the .proto files. CompilerModule:proto_compile(Config, AppFile, Protos) end. clean(Config, AppFile) -> %% Find a compiler for protocol buffers, %% use that for clean protocol buffers {CompilerModule, ProtoDirs} = select_proto_compiler_and_dir(Config), %% Get a list of generated .beam and .hrl files and then delete them case find_proto_files(ProtoDirs) of [] -> ok; Protos -> %% Ask the proto compiler to clean the .proto files. CompilerModule:proto_clean(Config, AppFile, Protos) end. %% =================================================================== %% Internal functions %% =================================================================== info(help, compile) -> info_help("Build protocol buffer (*.proto) sources", compile); info(help, clean) -> info_help("Delete protocol buffer (*.proto) build results", clean). info_help(GeneralDescr, Cmd) -> ?CONSOLE( "~s.~n" ++ "~n" ++ "Valid rebar.config options:~n" ++ " {proto_opts, [~n" ++ " {compiler, Compiler},~n" ++ " {src_dirs, [Dir]}~n" ++ " ]}~n" ++ "The following protocol buffer compilers are available:~n" ++ "~s~n", [GeneralDescr, format_proto_compiler_list()]), %% Print info for each proto compiler _ = [begin ?CONSOLE("--- ~p ---~n", [Key]), CompilerModule:proto_info(help, Cmd) end || #proto_compiler{key=Key, module=CompilerModule} <- find_proto_compilers()], ok. get_default_compiler() -> protobuffs. find_proto_compilers() -> {ok, RebarModuleGroups} = application:get_env(rebar, modules), {app_dir, Modules} = lists:keyfind(app_dir, 1, RebarModuleGroups), [#proto_compiler{key = M:key(), module = M} || M <- Modules, is_proto_compiler_module(M)]. is_proto_compiler_module(Module) -> case code:ensure_loaded(Module) of {module, Module} -> lists:all(fun({Function, Arity}) -> erlang:function_exported(Module, Function, Arity) end, [{key, 0}, {proto_compile, 3}, {proto_clean, 3}, {proto_info, 2}]); _ -> false end. select_proto_compiler_and_dir(Config) -> Default = get_default_compiler(), ProtoOpts = rebar_config:get_local(Config, proto_opts, []), Key = proplists:get_value(compiler, ProtoOpts, Default), ProtoDirs = proplists:get_value(src_dirs, ProtoOpts, ["src"]), AvailCompilers = find_proto_compilers(), CompilerModule = case lists:keyfind(Key, #proto_compiler.key, AvailCompilers) of #proto_compiler{module=Module} -> Module; false -> ?ABORT("No such protocol buffer compiler known, '~s'~n" ++ "The following are known:~n" ++ "~s~n", [Key, format_proto_compiler_list()]) end, {CompilerModule, ProtoDirs}. format_proto_compiler_list() -> Default = get_default_compiler(), Keys = [Key || #proto_compiler{key=Key} <- find_proto_compilers()], Annotations = [if Key == Default -> " (default)"; true -> "" end || Key <- Keys], lists:flatten( [?FMT(" ~p~s~n", [Key, Annotation]) || {Key, Annotation} <- lists:zip(Keys, Annotations)]). rebar-2.6.0/src/rebar_proto_gpb_compiler.erl000066400000000000000000000125331254103774400211600ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tomas Abrahamsson (tomas.abrahamsson@gmail.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_proto_gpb_compiler). -export([key/0, proto_compile/3, proto_clean/3]). %% for internal use only -export([proto_info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== key() -> gpb. proto_compile(Config, _AppFile, Files) -> %% Check for gpb library -- if it's not present, fail %% since we have.proto files that need building case gpb_is_present() of true -> GpbOpts = user_gpb_opts(Config), Targets = [filename:join("src", target_filename(F, GpbOpts)) || F <- Files], rebar_base_compiler:run(Config, [], lists:zip(Files, Targets), fun compile_gpb/3, [{check_last_mod, true}]); false -> ?ERROR("The gpb library is not present in code path!\n", []), ?FAIL end. target_filename(ProtoFileName, GpbOpts) -> ModulePrefix = proplists:get_value(module_name_prefix, GpbOpts, ""), ModuleSuffix = proplists:get_value(module_name_suffix, GpbOpts, ""), Base = filename:basename(ProtoFileName, ".proto"), ModulePrefix ++ Base ++ ModuleSuffix ++ ".erl". proto_clean(Config, _AppFile, ProtoFiles) -> GpbOpts = user_gpb_opts(Config) ++ default_dest_opts(), rebar_file_utils:delete_each( [beam_file(F, GpbOpts) || F <- ProtoFiles] ++ [erl_file(F, GpbOpts) || F <- ProtoFiles] ++ [hrl_file(F, GpbOpts) || F <- ProtoFiles]), ok. %% =================================================================== %% Internal functions %% =================================================================== proto_info(help, compile) -> ?CONSOLE( " gpb_opts is passed as options to gpb_compile:file/2.~n" " erl_opts is used when compiling the generated erlang files,~n" " so you might want to add an include path to gpb here,~n" " for gpb.hrl, or else use the include_as_lib gpb_opts option,~n" " or the defs_as_proplists gpb_opts option.~n", []); proto_info(help, clean) -> ?CONSOLE("", []). gpb_is_present() -> code:which(gpb) =/= non_existing. user_gpb_opts(Config) -> rebar_config:get_local(Config, gpb_opts, []). default_dest_opts() -> [{o_erl, "src"}, {o_hrl, "include"}]. compile_gpb(Source, _Target, Config) -> SourceFullPath = filename:absname(Source), GpbOpts = user_gpb_opts(Config) ++ default_dest_opts() ++ default_include_opts(SourceFullPath), ok = filelib:ensure_dir(filename:join("ebin", "dummy")), ok = filelib:ensure_dir(filename:join("include", "dummy")), case gpb_compile:file(SourceFullPath, GpbOpts) of ok -> ok; {error, Reason} -> ReasonStr = gpb_compile:format_error(Reason), ?ERROR("Failed to compile ~s: ~s~n", [SourceFullPath, ReasonStr]), ?FAIL end. default_include_opts(SourceFullPath) -> [{i,filename:dirname(SourceFullPath)}]. beam_file(ProtoFile, GpbOpts) -> proto_filename_to_path("ebin", ProtoFile, ".beam", GpbOpts). erl_file(ProtoFile, GpbOpts) -> ErlOutDir = get_erl_outdir(GpbOpts), proto_filename_to_path(ErlOutDir, ProtoFile, ".erl", GpbOpts). hrl_file(ProtoFile, GpbOpts) -> HrlOutDir = get_hrl_outdir(GpbOpts), proto_filename_to_path(HrlOutDir, ProtoFile, ".hrl", GpbOpts). proto_filename_to_path(Dir, ProtoFile, NewExt, GpbOpts) -> BaseNoExt = filename:basename(ProtoFile, ".proto"), Prefix = proplists:get_value(module_name_prefix, GpbOpts, ""), Suffix = proplists:get_value(module_name_suffix, GpbOpts, ""), filename:join([Dir, Prefix ++ BaseNoExt ++ Suffix ++ NewExt]). get_erl_outdir(Opts) -> proplists:get_value(o_erl, Opts, get_outdir(Opts)). get_hrl_outdir(Opts) -> proplists:get_value(o_hrl, Opts, get_outdir(Opts)). get_outdir(Opts) -> proplists:get_value(o, Opts, "."). rebar-2.6.0/src/rebar_protobuffs_compiler.erl000066400000000000000000000120721254103774400213540ustar00rootroot00000000000000%% -*- 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([key/0, proto_compile/3, proto_clean/3]). %% for internal use only -export([proto_info/2]). -include("rebar.hrl"). %% =================================================================== %% Public API %% =================================================================== key() -> protobuffs. proto_compile(Config, _AppFile, ProtoFiles) -> %% 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 <- ProtoFiles], %% Compile each proto file compile_each(Config, Targets); false -> ?ERROR("Protobuffs library not present in code path!\n", []), ?FAIL end. proto_clean(_Config, _AppFile, ProtoFiles) -> %% Get a list of generated .beam and .hrl files and then delete them BeamFiles = [fq_beam_file(F) || F <- ProtoFiles], HrlFiles = [fq_hrl_file(F) || F <- ProtoFiles], Targets = BeamFiles ++ HrlFiles, delete_each(Targets). %% =================================================================== %% Internal functions %% =================================================================== proto_info(help, compile) -> info_help(); proto_info(help, clean) -> info_help(). info_help() -> ?CONSOLE( "Valid rebar.config options:~n" " erl_opts is passed as compile_flags to " "protobuffs_compile:scan_file/2~n", []). 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(Config, [{Proto, Beam, Hrl} | Rest]) -> case needs_compile(Proto, Beam) of true -> ?CONSOLE("Compiling ~s\n", [Proto]), ErlOpts = rebar_utils:erl_opts(Config), case protobuffs_compile:scan_file(Proto, [{compile_flags,ErlOpts}]) 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("Protobuffs compile of ~s failed: ~p\n", [Proto, Other]), ?FAIL end; false -> ok end, compile_each(Config, 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.6.0/src/rebar_qc.erl000066400000000000000000000154571254103774400157060ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2011-2014 Tuncer Ayaz %% %% 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_qc). -export([qc/2, triq/2, eqc/2, clean/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). -define(QC_DIR, ".qc"). %% =================================================================== %% Public API %% =================================================================== qc(Config, _AppFile) -> ?CONSOLE("NOTICE: Using experimental 'qc' command~n", []), run_qc(Config, qc_opts(Config)). triq(Config, _AppFile) -> ?CONSOLE("NOTICE: Using experimental 'triq' command~n", []), ok = load_qc_mod(triq), run_qc(Config, qc_opts(Config), triq). eqc(Config, _AppFile) -> ?CONSOLE("NOTICE: Using experimental 'eqc' command~n", []), ok = load_qc_mod(eqc), run_qc(Config, qc_opts(Config), eqc). clean(_Config, _File) -> rebar_file_utils:rm_rf(?QC_DIR). %% =================================================================== %% Internal functions %% =================================================================== info(help, qc) -> ?CONSOLE( "Test QuickCheck properties.~n" "~n" "Valid rebar.config options:~n" " {qc_opts, [{qc_mod, module()}, Options]}~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n" "Valid command line options:~n" " compile_only=true (Compile but do not test properties)", [ {qc_compile_opts, []}, {qc_first_files, []}, {cover_enabled, false}, {cover_print_enabled, false}, {cover_export_enabled, false} ]); info(help, clean) -> Description = ?FMT("Delete QuickCheck test dir (~s)", [?QC_DIR]), ?CONSOLE("~s.~n", [Description]). -define(TRIQ_MOD, triq). -define(EQC_MOD, eqc). qc_opts(Config) -> rebar_config:get(Config, qc_opts, []). run_qc(Config, QCOpts) -> run_qc(Config, QCOpts, select_qc_mod(QCOpts)). run_qc(Config, RawQCOpts, QC) -> ?DEBUG("Selected QC module: ~p~n", [QC]), QCOpts = lists:filter(fun({qc_mod, _}) -> false; (_) -> true end, RawQCOpts), run(Config, QC, QCOpts). select_qc_mod(QCOpts) -> case proplists:get_value(qc_mod, QCOpts) of undefined -> detect_qc_mod(); QC -> case code:ensure_loaded(QC) of {module, QC} -> QC; {error, nofile} -> ?ABORT("Configured QC library '~p' not available~n", [QC]) end end. detect_qc_mod() -> case code:ensure_loaded(?TRIQ_MOD) of {module, ?TRIQ_MOD} -> ?TRIQ_MOD; {error, nofile} -> case code:ensure_loaded(?EQC_MOD) of {module, ?EQC_MOD} -> ?EQC_MOD; {error, nofile} -> ?ABORT("No QC library available~n", []) end end. load_qc_mod(Mod) -> case code:ensure_loaded(Mod) of {module, Mod} -> ok; {error, nofile} -> ?ABORT("Failed to load QC lib '~p'~n", [Mod]) end. ensure_dirs() -> ok = filelib:ensure_dir(filename:join(qc_dir(), "dummy")), ok = filelib:ensure_dir(filename:join(rebar_utils:ebin_dir(), "dummy")). setup_codepath() -> CodePath = code:get_path(), true = code:add_patha(qc_dir()), true = code:add_pathz(rebar_utils:ebin_dir()), CodePath. qc_dir() -> filename:join(rebar_utils:get_cwd(), ?QC_DIR). run(Config, QC, QCOpts) -> ?DEBUG("qc_opts: ~p~n", [QCOpts]), ok = ensure_dirs(), CodePath = setup_codepath(), CompileOnly = rebar_config:get_global(Config, compile_only, false), %% Compile erlang code to ?QC_DIR, using a tweaked config %% with appropriate defines, and include all the test modules %% as well. {ok, SrcErls} = rebar_erlc_compiler:test_compile(Config, "qc", ?QC_DIR), case CompileOnly of "true" -> true = rebar_utils:cleanup_code_path(CodePath), ?CONSOLE("Compiled modules for qc~n", []); false -> run1(QC, QCOpts, Config, CodePath, SrcErls) end. run1(QC, QCOpts, Config, CodePath, SrcErls) -> AllBeamFiles = rebar_utils:beams(?QC_DIR), AllModules = [rebar_utils:beam_to_mod(?QC_DIR, N) || N <- AllBeamFiles], PropMods = find_prop_mods(), FilteredModules = AllModules -- PropMods, SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls], {ok, CoverLog} = rebar_cover_utils:init(Config, AllBeamFiles, qc_dir()), TestModule = fun(M) -> qc_module(QC, QCOpts, M) end, QCResult = lists:flatmap(TestModule, PropMods), rebar_cover_utils:perform_cover(Config, FilteredModules, SrcModules, qc_dir()), rebar_cover_utils:close(CoverLog), ok = rebar_cover_utils:exit(), true = rebar_utils:cleanup_code_path(CodePath), case QCResult of [] -> ok; Errors -> ?ABORT("One or more QC properties didn't hold true:~n~p~n", [Errors]) end. qc_module(QC=triq, _QCOpts, M) -> case QC:module(M) of true -> []; Failed -> [Failed] end; qc_module(QC=eqc, [], M) -> QC:module(M); qc_module(QC=eqc, QCOpts, M) -> QC:module(QCOpts, M). find_prop_mods() -> Beams = rebar_utils:find_files_by_ext(?QC_DIR, ".beam"), [M || M <- [rebar_utils:erl_to_mod(Beam) || Beam <- Beams], has_prop(M)]. has_prop(Mod) -> lists:any(fun({F,_A}) -> lists:prefix("prop_", atom_to_list(F)) end, Mod:module_info(exports)). rebar-2.6.0/src/rebar_rel_utils.erl000066400000000000000000000227221254103774400172760ustar00rootroot00000000000000%% -*- 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/1, get_rel_file_path/2, get_rel_file_path/3, load_config/2, get_sys_tuple/1, get_excl_lib_tuple/1, get_target_dir/2, get_root_dir/2, get_target_parent_dir/2]). -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) -> PVer = get_permanent_version(Path), get_rel_file_path(Name, Path, PVer). get_rel_file_path(Name, Path, Version) -> Dir = filename:join([Path, "releases", Version]), Path1 = filename:join([Dir, Name ++ "_" ++ Version ++".rel"]), Path2 = filename:join([Dir, Name ++ ".rel"]), case {filelib:is_file(Path1), filelib:is_file(Path2)} of {true, _} -> Path1; {_, true} -> Path2; _ -> ?ABORT("can not find .rel file for version ~p~n", [Version]) end. %% Get the previous release path from a global variable get_previous_release_path(Config) -> case rebar_config:get_global(Config, 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(Config, ReltoolFile) -> case rebar_config:consult_file(ReltoolFile) of {ok, Terms} -> expand_version(Config, 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~n", []) end. %% %% Look for the {excl_lib, ...} tuple in sys tuple of the reltool.config file. %% Without this present, return false. %% get_excl_lib_tuple(ReltoolConfig) -> lists:keyfind(excl_lib, 1, element(2, get_sys_tuple(ReltoolConfig))). %% %% Look for {target_dir, TargetDir} in the reltool config file; if none is %% found, use the name of the release as the default target directory. %% get_target_dir(Config, ReltoolConfig) -> case rebar_config:get_global(Config, 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(Config, ReltoolConfig) -> TargetDir = get_target_dir(Config, 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(Config, ReltoolConfig) -> {sys, SysInfo} = get_sys_tuple(ReltoolConfig), SysRootDirTuple = lists:keyfind(root_dir, 1, SysInfo), CmdRootDir = rebar_config:get_global(Config, 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(Config, ReltoolConfig, Dir) -> case lists:keyfind(sys, 1, ReltoolConfig) of {sys, Sys} -> {Config1, Rels} = lists:foldl( fun(Term, {C, R}) -> {C1, Rel} = expand_rel_version(C, Term, Dir), {C1, [Rel|R]} end, {Config, []}, Sys), ExpandedSys = {sys, lists:reverse(Rels)}, {Config1, lists:keyreplace(sys, 1, ReltoolConfig, ExpandedSys)}; _ -> {Config, ReltoolConfig} end. expand_rel_version(Config, {rel, Name, Version, Apps}, Dir) -> {NewConfig, VsnString} = rebar_utils:vcs_vsn(Config, Version, Dir), {NewConfig, {rel, Name, VsnString, Apps}}; expand_rel_version(Config, Other, _Dir) -> {Config, Other}. %% get permanent version from start_erl.data get_permanent_version(Path) -> DataFile = filename:join([Path, "releases", "start_erl.data"]), case file:read_file(DataFile) of {ok, DataBin} -> [_, Version] = string:tokens( string:strip(binary_to_list(DataBin), right, $\n), " "), Version; {error, enoent} -> ?ABORT("~s is missing~n", [DataFile]) end. rebar-2.6.0/src/rebar_reltool.erl000066400000000000000000000422601254103774400167530ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). %% =================================================================== %% Public API %% =================================================================== generate(Config0, ReltoolFile) -> %% Make sure we have decent version of reltool available check_vsn(), %% Load the reltool configuration from the file {Config, ReltoolConfig} = rebar_rel_utils:load_config(Config0, 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, Config}; {error, failed} -> ?FAIL; Other2 -> ?ERROR("Unexpected error: ~p\n", [Other2]), ?FAIL end. overlay(Config, ReltoolFile) -> %% Load the reltool configuration from the file {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile), {process_overlay(Config, ReltoolConfig), Config1}. clean(Config, ReltoolFile) -> {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile), TargetDir = rebar_rel_utils:get_target_dir(Config, ReltoolConfig), rebar_file_utils:rm_rf(TargetDir), rebar_file_utils:delete_each(["reltool.spec"]), {ok, Config1}. %% =================================================================== %% Internal functions %% =================================================================== info(help, generate) -> info_help("Build release with reltool"); info(help, clean) -> info_help("Delete release"); info(help, overlay) -> info_help("Run reltool overlays only"). info_help(Description) -> ?CONSOLE( "~s.~n" "~n" "Valid rebar.config options:~n" " ~n" "Valid reltool.config options:~n" " {sys, []}~n" " {target_dir, \"target\"}~n" " {overlay_vars, \"overlay\"}~n" " {overlay, []}~n" "Valid command line options:~n" " target_dir=target~n" " overlay_vars=VarsFile~n" " dump_spec=1 (write reltool target spec to reltool.spec)~n", [ Description ]). check_vsn() -> %% TODO: use application:load and application:get_key once we require %% R14A or newer. There's no reltool.app before R14A. case code:lib_dir(reltool) of {error, bad_name} -> ?ABORT("Reltool support requires the reltool application " "to be installed!", []); Path -> ReltoolVsn = reltool_vsn(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. reltool_vsn(ReltoolPath) -> AppFile = filename:join([ReltoolPath, "ebin", "reltool.app"]), case filelib:is_regular(AppFile) of true -> reltool_vsn_from_app(); false -> reltool_vsn_from_path(ReltoolPath) end. reltool_vsn_from_app() -> ok = case application:load(reltool) of ok -> ok; {error, {already_loaded, reltool}} -> ok end, {ok, Vsn} = application:get_key(reltool, vsn), "reltool-" ++ Vsn. %% NOTE: OTP releases prior to R14B did not install %% lib/reltool-x.y.z/ebin/reltool.app. Therefore, if we cannot find the app %% file, we have to resort to getting the version string from reltool's lib_dir %% path. This usually works, but as reported in %% https://github.com/rebar/rebar/issues/415 there can be installations without %% version strings in lib_dir paths. Given R13's age and that it's unusual to %% have vsn-less lib_dir paths, this shouldn't be a problem. %% %% TODO: Once we require at least R14B04 (didn't check for existence of %% ebin/reltool.app in R14 releases older than R14B04), simplify reltool_vsn/1 %% to get the version string only from the app key. reltool_vsn_from_path(ReltoolPath) -> filename:basename(ReltoolPath). process_overlay(Config, ReltoolConfig) -> TargetDir = rebar_rel_utils:get_target_dir(Config, 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}, {hostname, net_adm:localhost()}]), %% Load up any variables specified by overlay_vars OverlayVars1 = overlay_vars(Config, OverlayVars0, ReltoolConfig), OverlayVars = rebar_templater:resolve_variables(dict:to_list(OverlayVars1), OverlayVars1), %% Finally, overlay the files specified by the overlay section case overlay_files(ReltoolConfig) of [] -> ok; Overlay -> execute_overlay(Overlay, OverlayVars, rebar_utils:get_cwd(), TargetDir) 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(Config, Vars0, ReltoolConfig) -> BaseVars = load_vars_file( [proplists:get_value(overlay_vars, ReltoolConfig)]), OverlayVars = rebar_config:get_global(Config, overlay_vars, []), OverrideVars = load_vars_file(string:tokens(OverlayVars, ",")), M = fun merge_overlay_var/3, dict:merge(M, dict:merge(M, Vars0, BaseVars), OverrideVars). merge_overlay_var(_Key, _Base, Override) -> Override. %% %% If a filename is provided, construct a dict of terms %% load_vars_file([undefined]) -> dict:new(); load_vars_file([]) -> dict:new(); load_vars_file(Files) -> load_vars_file(Files, dict:new()). load_vars_file([], Dict) -> Dict; load_vars_file([File | Files], BaseVars) -> case rebar_config:consult_file(File) of {ok, Terms} -> OverrideVars = dict:from_list(Terms), M = fun merge_overlay_var/3, load_vars_file(Files, dict:merge(M, BaseVars, OverrideVars)); {error, Reason} -> ?ABORT("Unable to load overlay_vars from ~p: ~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(Config, ReltoolConfig), mk_target_dir(Config, TargetDir), %% Determine the otp root dir to use RootDir = rebar_rel_utils:get_root_dir(Config, ReltoolConfig), %% Dump the spec, if necessary dump_spec(Config, 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(Config, ReltoolConfig); {error, Reason} -> ?ABORT("Unable to generate spec: ~s\n", [Reason]) end. mk_target_dir(Config, 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(Config, 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(Config, Spec) -> case rebar_config:get_global(Config, 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. overlay_files(ReltoolConfig) -> Original = case lists:keyfind(overlay, 1, ReltoolConfig) of {overlay, Overlay} when is_list(Overlay) -> Overlay; false -> ?INFO("No {overlay, [...]} found in reltool.config.\n", []), []; _ -> ?ABORT("{overlay, [...]} entry in reltool.config " "must be a list.\n", []) end, SlimAddition = case rebar_rel_utils:get_excl_lib_tuple(ReltoolConfig) of {excl_lib, otp_root} -> [{create, "releases/{{rel_vsn}}/runner_script.data", "slim\n"}]; false -> [] end, Original ++ SlimAddition. %% TODO: Merge functionality here with rebar_templater execute_overlay([], _Vars, _BaseDir, _TargetDir) -> 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"), RelFile = filename:join([ReleasesDir, RelVsn, RelName ++ ".rel"]), Apps = rebar_rel_utils:get_rel_apps(RelFile), TargetLib = filename:join(TargetDir,"lib"), AppDirs = [ {App, Vsn, TargetLib} || {App, Vsn} <- Apps, filelib:is_dir( filename:join(TargetLib, lists:concat([App, "-", Vsn]))) ], case release_handler:create_RELEASES( code:root_dir(), ReleasesDir, RelFile, AppDirs) of ok -> ok; {error, Reason} -> ?ABORT("Failed to create RELEASES file: ~p\n", [Reason]) end. rebar-2.6.0/src/rebar_require_vsn.erl000066400000000000000000000121021254103774400176250ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2, version_tuple/3]). %% =================================================================== %% Public API %% =================================================================== compile(Config, _) -> check_versions(Config). eunit(Config, _) -> check_versions(Config). %% ==================================================================== %% Internal functions %% ==================================================================== info(help, compile) -> info_help(); info(help, eunit) -> info_help(). info_help() -> ?CONSOLE( "Check required ERTS or OTP release version.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n", [ {require_erts_vsn, ".*"}, {require_otp_vsn, ".*"}, {require_min_otp_vsn, ".*"} ]). check_versions(Config) -> ShouldAbort = case rebar_config:get_xconf(Config, keep_going, false) of true -> keep_going; false -> abort end, 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 -> maybe_abort( ShouldAbort, "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 -> maybe_abort( ShouldAbort, "OTP release ~s does not match required regex ~s\n", [erlang:system_info(otp_release), OtpRegex]) end, case rebar_config:get(Config, require_min_otp_vsn, undefined) of undefined -> ?DEBUG("Min OTP version unconfigured~n", []); MinOtpVsn -> {MinMaj, MinMin} = version_tuple(ShouldAbort, MinOtpVsn, "configured"), {OtpMaj, OtpMin} = version_tuple(ShouldAbort, erlang:system_info(otp_release), "OTP Release"), case {OtpMaj, OtpMin} >= {MinMaj, MinMin} of true -> ?DEBUG("~s satisfies the requirement for vsn ~s~n", [erlang:system_info(otp_release), MinOtpVsn]); false -> maybe_abort( ShouldAbort, "OTP release ~s or later is required, you have: ~s~n", [MinOtpVsn, erlang:system_info(otp_release)]) end end. version_tuple(ShouldAbort, OtpRelease, Type) -> case re:run(OtpRelease, "R?(\\d+)B?-?(\\d+)?", [{capture, all, list}]) of {match, [_Full, Maj, Min]} -> {list_to_integer(Maj), list_to_integer(Min)}; {match, [_Full, Maj]} -> {list_to_integer(Maj), 0}; nomatch -> maybe_abort(ShouldAbort, "Cannot parse ~s version string: ~s~n", [Type, OtpRelease]) end. maybe_abort(abort, Format, Data) -> ?ABORT(Format, Data); maybe_abort(keep_going, Format, Data) -> ?ERROR(Format, Data). rebar-2.6.0/src/rebar_shell.erl000066400000000000000000000075631254103774400164110ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2011 Trifork %% %% 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_shell). -author("Kresten Krab Thorup "). -include("rebar.hrl"). -export([shell/2, info/2]). %% NOTE: %% this is an attempt to replicate `erl -pa ./ebin -pa deps/*/ebin`. it is %% mostly successful but does stop and then restart the user io system to get %% around issues with rebar being an escript and starting in `noshell` mode. %% it also lacks the ctrl-c interrupt handler that `erl` features. ctrl-c will %% immediately kill the script. ctrl-g, however, works fine shell(_Config, _AppFile) -> true = code:add_pathz(rebar_utils:ebin_dir()), %% scan all processes for any with references to the old user and save them to %% update later NeedsUpdate = [Pid || Pid <- erlang:processes(), proplists:get_value(group_leader, erlang:process_info(Pid)) == whereis(user) ], %% terminate the current user ok = supervisor:terminate_child(kernel_sup, user), %% start a new shell (this also starts a new user under the correct group) _ = user_drv:start(), %% wait until user_drv and user have been registered (max 3 seconds) ok = wait_until_user_started(3000), %% set any process that had a reference to the old user's group leader to the %% new user process _ = [erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate], %% enable error_logger's tty output ok = error_logger:swap_handler(tty), %% disable the simple error_logger (which may have been added multiple %% times). removes at most the error_logger added by init and the %% error_logger added by the tty handler ok = remove_error_handler(3), %% this call never returns (until user quits shell) timer:sleep(infinity). info(help, shell) -> ?CONSOLE( "Start a shell with project and deps preloaded similar to~n" "'erl -pa ebin -pa deps/*/ebin'.~n", [] ). remove_error_handler(0) -> ?WARN("Unable to remove simple error_logger handler~n", []); remove_error_handler(N) -> case gen_event:delete_handler(error_logger, error_logger, []) of {error, module_not_found} -> ok; {error_logger, _} -> remove_error_handler(N-1) end. %% Timeout is a period to wait before giving up wait_until_user_started(0) -> ?ABORT("Timeout exceeded waiting for `user` to register itself~n", []), erlang:error(timeout); wait_until_user_started(Timeout) -> case whereis(user) of %% if user is not yet registered wait a tenth of a second and try again undefined -> timer:sleep(100), wait_until_user_started(Timeout - 100); _ -> ok end.rebar-2.6.0/src/rebar_subdirs.erl000066400000000000000000000065401254103774400167470ustar00rootroot00000000000000%% -*- 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_config:is_skip_dir(Config, 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.6.0/src/rebar_templater.erl000066400000000000000000000453441254103774400172760ustar00rootroot00000000000000%% -*- 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-lib'/2, 'create-node'/2, 'list-templates'/2, create/2]). %% API for other utilities that need templating functionality -export([resolve_variables/2, render/2]). %% for internal use only -export([info/2]). -include("rebar.hrl"). -define(TEMPLATE_RE, "^[^._].*\\.template\$"). %% =================================================================== %% Public API %% =================================================================== 'create-app'(Config, _File) -> %% Alias for create w/ template=simpleapp create1(Config, "simpleapp"). 'create-lib'(Config, _File) -> %% Alias for create w/ template=simplelib create1(Config, "simplelib"). 'create-node'(Config, _File) -> %% Alias for create w/ template=simplenode create1(Config, "simplenode"). 'list-templates'(Config, _File) -> {AvailTemplates, Files} = find_templates(Config), ?DEBUG("Available templates: ~p\n", [AvailTemplates]), lists:foreach( fun({Type, F}) -> BaseName = filename:basename(F, ".template"), TemplateTerms = consult(load_file(Files, Type, F)), {_, VarList} = lists:keyfind(variables, 1, TemplateTerms), Vars = lists:foldl(fun({V,_}, Acc) -> [atom_to_list(V) | Acc] end, [], VarList), ?CONSOLE(" * ~s: ~s (~p) (variables: ~p)\n", [BaseName, F, Type, string:join(Vars, ", ")]) end, AvailTemplates), ok. create(Config, _) -> TemplateId = template_id(Config), create1(Config, TemplateId). %% %% 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([{Key, {list, Dicts}} | Rest], Dict) when is_list(Dicts) -> %% just un-tag it so mustache can use it resolve_variables(Rest, dict:store(Key, Dicts, 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), rebar_mustache:render(Str1, Context). %% =================================================================== %% Internal functions %% =================================================================== info(help, create) -> ?CONSOLE( "Create skel based on template and vars.~n" "~n" "Valid command line options:~n" " template= [var=foo,...]~n", []); info(help, 'create-app') -> ?CONSOLE( "Create simple app skel.~n" "~n" "Valid command line options:~n" " [appid=myapp]~n", []); info(help, 'create-lib') -> ?CONSOLE( "Create simple lib skel.~n" "~n" "Valid command line options:~n" " [libid=mylib]~n", []); info(help, 'create-node') -> ?CONSOLE( "Create simple node skel.~n" "~n" "Valid command line options:~n" " [nodeid=mynode]~n", []); info(help, 'list-templates') -> ?CONSOLE("List available templates.~n", []). create1(Config, TemplateId) -> {AvailTemplates, Files} = find_templates(Config), ?DEBUG("Available templates: ~p\n", [AvailTemplates]), %% 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(Files, 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(Config, template_vars, undefined) of undefined -> Context0; File -> case consult(load_file([], file, File)) of {error, Reason} -> ?ABORT("Unable to load template_vars from ~s: ~p\n", [File, Reason]); 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)) 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(Config, 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(Files, Type, Template), Context)), ?DEBUG("Final template def ~p: ~p\n", [TemplateId, FinalTemplate]), %% Execute the instructions in the finalized template Force = rebar_config:get_global(Config, force, "0"), execute_template(Files, FinalTemplate, Type, Template, Context, Force, []). find_templates(Config) -> %% Load a list of all the files in the escript -- cache them since %% we'll potentially need to walk it several times over the course of %% a run. Files = cache_escript_files(Config), %% Build a list of available templates AvailTemplates = find_disk_templates(Config) ++ find_escript_templates(Files), {AvailTemplates, Files}. %% %% Scan the current escript for available files %% cache_escript_files(Config) -> {ok, Files} = rebar_utils:escript_foldl( fun(Name, _, GetBin, Acc) -> [{Name, GetBin()} | Acc] end, [], rebar_config:get_xconf(Config, escript)), Files. template_id(Config) -> case rebar_config:get_global(Config, template, undefined) of undefined -> ?ABORT("No template specified.\n", []); TemplateId -> TemplateId end. find_escript_templates(Files) -> [{escript, Name} || {Name, _Bin} <- Files, re:run(Name, ?TEMPLATE_RE, [{capture, none}]) == match]. find_disk_templates(Config) -> OtherTemplates = find_other_templates(Config), HomeTemplates = filename:join([os:getenv("HOME"), ".rebar", "templates"]), HomeFiles = rebar_utils:find_files_by_ext(HomeTemplates, ".template"), Recursive = rebar_config:is_recursive(Config), LocalFiles = rebar_utils:find_files_by_ext(".", ".template", Recursive), [{file, F} || F <- OtherTemplates ++ HomeFiles ++ LocalFiles]. find_other_templates(Config) -> case rebar_config:get_global(Config, template_dir, undefined) of undefined -> []; TemplateDir -> rebar_utils:find_files_by_ext(TemplateDir, ".template") 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(Files, escript, Name) -> {Name, Bin} = lists:keyfind(Name, 1, Files), Bin; load_file(_Files, 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(_Config, [], Dict) -> Dict; update_vars(Config, [Key | Rest], Dict) -> Value = rebar_config:get_global(Config, Key, dict:fetch(Key, Dict)), update_vars(Config, Rest, dict:store(Key, Value, Dict)). %% %% Given a string or binary, parse it into a list of terms, ala file:consult/1 %% 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, [maybe_dict(Term) | Acc]); {eof, _Other} -> lists:reverse(Acc); {error, Info, _} -> {error, Info} end; {more, Cont1} -> consult(Cont1, eof, Acc) end. maybe_dict({Key, {list, Dicts}}) -> %% this is a 'list' element; a list of lists representing dicts {Key, {list, [dict:from_list(D) || D <- Dicts]}}; maybe_dict(Term) -> Term. 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. prepend_instructions(Instructions, Rest) when is_list(Instructions) -> Instructions ++ Rest; prepend_instructions(Instruction, Rest) -> [Instruction|Rest]. %% %% Execute each instruction in a template definition file. %% execute_template(_Files, [], _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 -f/--force/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(Files, [{'if', Cond, True} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> execute_template(Files, [{'if', Cond, True, []}|Rest], TemplateType, TemplateName, Context, Force, ExistingFiles); execute_template(Files, [{'if', Cond, True, False} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> Instructions = case dict:find(Cond, Context) of {ok, true} -> True; {ok, "true"} -> True; _ -> False end, execute_template(Files, prepend_instructions(Instructions, Rest), TemplateType, TemplateName, Context, Force, ExistingFiles); execute_template(Files, [{'case', Variable, Values, Instructions} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> {ok, Value} = dict:find(Variable, Context), Instructions2 = case lists:member(Value, Values) of true -> Instructions; _ -> [] end, execute_template(Files, prepend_instructions(Instructions2, Rest), TemplateType, TemplateName, Context, Force, ExistingFiles); execute_template(Files, [{template, Input, Output} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> InputName = filename:join(filename:dirname(TemplateName), Input), File = load_file(Files, TemplateType, InputName), case write_file(Output, render(File, Context), Force) of ok -> execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, exists} -> execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, [Output|ExistingFiles]) end; execute_template(Files, [{file, Input, Output} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> InputName = filename:join(filename:dirname(TemplateName), Input), File = load_file(Files, TemplateType, InputName), case write_file(Output, File, Force) of ok -> execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, exists} -> execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, [Output|ExistingFiles]) end; execute_template(Files, [{dir, Name} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> case filelib:ensure_dir(filename:join(Name, "dummy")) of ok -> execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); {error, Reason} -> ?ABORT("Failed while processing template instruction " "{dir, ~s}: ~p\n", [Name, Reason]) end; execute_template(Files, [{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(Files, Rest, TemplateType, TemplateName, Context, Force, ExistingFiles) catch _:_ -> ?ABORT("Failed while processing template instruction " "{copy, ~s, ~s}~n", [Input, Output]) end; execute_template(Files, [{chmod, Mod, File} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) when is_integer(Mod) -> case file:change_mode(File, Mod) of ok -> execute_template(Files, 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(Files, [{symlink, Existing, New} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> case file:make_symlink(Existing, New) of ok -> execute_template(Files, 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(Files, [{variables, _} | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, ExistingFiles); execute_template(Files, [Other | Rest], TemplateType, TemplateName, Context, Force, ExistingFiles) -> ?WARN("Skipping unknown template instruction: ~p\n", [Other]), execute_template(Files, Rest, TemplateType, TemplateName, Context, Force, ExistingFiles). rebar-2.6.0/src/rebar_upgrade.erl000066400000000000000000000246571254103774400167340ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). -define(TMP, "_tmp"). %% ==================================================================== %% Public API %% ==================================================================== 'generate-upgrade'(Config0, ReltoolFile) -> %% Get the old release path {Config, ReltoolConfig} = rebar_rel_utils:load_config(Config0, ReltoolFile), TargetParentDir = rebar_rel_utils:get_target_parent_dir(Config, ReltoolConfig), TargetDir = rebar_rel_utils:get_target_dir(Config, ReltoolConfig), PrevRelPath = rebar_rel_utils:get_previous_release_path(Config), OldVerPath = filename:join([TargetParentDir, PrevRelPath]), %% Run checks to make sure that building a package is possible {NewVerPath, NewName, NewVer, OldVer} = run_checks(Config, OldVerPath, ReltoolConfig), NameVer = NewName ++ "_" ++ NewVer, OldRelName = get_old_rel_name(OldVerPath, OldVer, NewName), %% 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, OldRelName), %% 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 = rebar_utils:cleanup_code_path(OrigPath), {ok, Config}. %% =================================================================== %% Internal functions %% ================================================================== info(help, 'generate-upgrade') -> ?CONSOLE("Build an upgrade package.~n" "~n" "Valid command line options:~n" " previous_release=path~n" " target_dir=target_dir (optional)~n", []). run_checks(Config, 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 = rebar_rel_utils:get_target_dir(Config, ReltoolConfig), 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, OldVer}. 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, OldRelName) -> Opts = [silent], NameList = [OldRelName], 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"])), SysConfig = filename:join([TargetDir, "releases", Ver, "sys.config"]), _ = case filelib:is_regular(SysConfig) of true -> {ok, _} = file:copy( SysConfig, filename:join([".", ?TMP, "releases", Ver, "sys.config"])); false -> ok end, VmArgs = filename:join([TargetDir, "releases", Ver, "vm.args"]), case filelib:is_regular(VmArgs) of true -> {ok, _} = file:copy( VmArgs, filename:join([".", ?TMP, "releases", Ver, "vm.args"])); false -> {ok, 0} end. 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. get_old_rel_name(OldVerPath, OldVer, Name) -> OldRelFile = rebar_rel_utils:get_rel_file_path(Name, OldVerPath, OldVer), filename:basename(OldRelFile, ".rel"). rebar-2.6.0/src/rebar_utils.erl000066400000000000000000000563771254103774400164510ustar00rootroot00000000000000%% -*- 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, sh_send/3, find_files/2, find_files/3, find_files_by_ext/2, find_files_by_ext/3, now_str/0, ensure_dir/1, beam_to_mod/2, beams/1, erl_to_mod/1, abort/0, abort/2, escript_foldl/3, find_executable/1, prop_check/3, expand_code_path/0, expand_env_variable/3, vcs_vsn/3, deprecated/3, deprecated/4, get_deprecated_global/4, get_deprecated_global/5, get_experimental_global/3, get_experimental_local/3, get_deprecated_list/4, get_deprecated_list/5, get_deprecated_local/4, get_deprecated_local/5, delayed_halt/1, erl_opts/1, src_dirs/1, ebin_dir/0, base_dir/1, processing_base_dir/1, processing_base_dir/2, patch_env/2, cleanup_code_path/1]). %% for internal use only -export([otp_release/0]). -dialyzer({no_missing_calls, escript_foldl/3}). -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(), 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. sh_send(Command0, String, Options0) -> ?INFO("sh_send info:\n\tcwd: ~p\n\tcmd: ~s < ~s\n", [get_cwd(), Command0, String]), ?DEBUG("\topts: ~p\n", [Options0]), DefaultOptions = [use_stdout, abort_on_error], Options = [expand_sh_flag(V) || V <- proplists:compact(Options0 ++ DefaultOptions)], 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), %% allow us to send some data to the shell command's STDIN %% Erlang doesn't let us get any reply after sending an EOF, though... Port ! {self(), {command, String}}, port_close(Port). %% %% 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], ?DEBUG("Port Cmd: ~p\nPort Opts: ~p\n", [Command, PortSettings]), Port = open_port({spawn, Command}, PortSettings), case sh_loop(Port, OutputHandler, []) of {ok, _Output} = Ok -> Ok; {error, {_Rc, _Output}=Err} -> ErrorHandler(Command, Err) 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, []). %% Find files by extension, for example ".erl", avoiding resource fork %% files in OS X. Such files are named for example src/._xyz.erl %% Such files may also appear with network filesystems on OS X. %% %% The Ext is really a regexp, with any leading dot implicitly %% escaped, and anchored at the end of the string. %% find_files_by_ext(Dir, Ext) -> find_files_by_ext(Dir, Ext, true). find_files_by_ext(Dir, Ext, Recursive) -> %% Convert simple extension to proper regex EscapeDot = case Ext of "." ++ _ -> "\\"; _ -> %% allow for other suffixes, such as _pb.erl "" end, ExtRe = "^[^._].*" ++ EscapeDot ++ Ext ++ [$$], find_files(Dir, ExtRe, Recursive). 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() -> no_return(). abort() -> throw(rebar_abort). -spec abort(string(), [term()]) -> no_return(). abort(String, Args) -> ?ERROR(String, Args), abort(). %% 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) -> Path1 = rmemo:call(filename, absname, [Path]), [Path1 | 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; _ -> ReOpts = [global, unicode, {return, list}], VarValue = re:replace(RawVarValue, "\\\\", "\\\\\\\\", ReOpts), %% Use a regex to match/replace: %% Given variable "FOO", match $FOO\W | $FOOeol | ${FOO}. RegEx = io_lib:format("\\\$(~s(\\W|$)|{~s})", [VarName, VarName]), re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts) end. vcs_vsn(Config, Vsn, Dir) -> Key = {Vsn, Dir}, Cache = rebar_config:get_xconf(Config, vsn_cache), case dict:find(Key, Cache) of error -> VsnString = vcs_vsn_1(Vsn, Dir), Cache1 = dict:store(Key, VsnString, Cache), Config1 = rebar_config:set_xconf(Config, vsn_cache, Cache1), {Config1, VsnString}; {ok, VsnString} -> {Config, VsnString} end. get_deprecated_global(Config, OldOpt, NewOpt, When) -> get_deprecated_global(Config, OldOpt, NewOpt, undefined, When). get_deprecated_global(Config, OldOpt, NewOpt, Default, When) -> get_deprecated_3(fun rebar_config:get_global/3, Config, OldOpt, NewOpt, Default, When). get_experimental_global(Config, Opt, Default) -> get_experimental_3(fun rebar_config:get_global/3, Config, Opt, Default). get_experimental_local(Config, Opt, Default) -> get_experimental_3(fun rebar_config:get_local/3, Config, Opt, Default). 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. %% @doc Return list of erl_opts -spec erl_opts(rebar_config:config()) -> list(). erl_opts(Config) -> RawErlOpts = filter_defines(rebar_config:get(Config, erl_opts, []), []), Defines = [{d, list_to_atom(D)} || D <- rebar_config:get_xconf(Config, defines, [])], Opts = Defines ++ RawErlOpts, case proplists:is_defined(no_debug_info, Opts) of true -> [O || O <- Opts, O =/= no_debug_info]; false -> [debug_info|Opts] end. -spec src_dirs([string()]) -> [file:filename(), ...]. src_dirs([]) -> ["src"]; src_dirs(SrcDirs) -> SrcDirs. ebin_dir() -> filename:join(get_cwd(), "ebin"). base_dir(Config) -> rebar_config:get_xconf(Config, base_dir). processing_base_dir(Config) -> Cwd = rebar_utils:get_cwd(), processing_base_dir(Config, Cwd). processing_base_dir(Config, Dir) -> AbsDir = filename:absname(Dir), AbsDir =:= base_dir(Config). %% @doc Returns the list of environment variables including 'REBAR' which %% points to the rebar executable used to execute the currently running %% command. The environment is not modified if rebar was invoked %% programmatically. -spec patch_env(rebar_config:config(), [{string(), string()}]) -> [{string(), string()}]. patch_env(Config, []) -> %% If we reached an empty list, the env did not contain the REBAR variable. case rebar_config:get_xconf(Config, escript, "") of "" -> % rebar was invoked programmatically []; Path -> [{"REBAR", Path}] end; patch_env(_Config, [{"REBAR", _} | _]=All) -> All; patch_env(Config, [E | Rest]) -> [E | patch_env(Config, Rest)]. %% ==================================================================== %% Internal functions %% ==================================================================== otp_release() -> rmemo:call(fun otp_release_1/1, [(erlang:system_info(otp_release))]). %% If OTP <= R16, otp_release is already what we want. otp_release_1([$R,N|_]=Rel) when is_integer(N) -> Rel; %% If OTP >= 17.x, erlang:system_info(otp_release) returns just the %% major version number, we have to read the full version from %% a file. See http://www.erlang.org/doc/system_principles/versions.html otp_release_1(Rel) -> Files = [ filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]), filename:join([code:root_dir(), "OTP_VERSION"]) ], %% It's possible that none of the above files exist on the filesystem, in %% which case, we're just going to rely on the provided "Rel" (which should %% just be the value of `erlang:system_info(otp_release)`). case read_otp_version_files(Files) of undefined -> warn_missing_otp_version_file(Rel), Rel; Vsn -> Vsn end. warn_missing_otp_version_file(Rel) -> ?WARN("No OTP_VERSION file found. Using version string ~p.~n", [Rel]). %% Try to open each file path provided, and if any of them exist on the %% filesystem, read their contents and return the value of the first one found. read_otp_version_files([]) -> undefined; read_otp_version_files([File | Rest]) -> case file:read_file(File) of {ok, Vsn} -> normalize_otp_version(Vsn); {error, enoent} -> read_otp_version_files(Rest) end. %% Takes the Version binary as read from the OTP_VERSION file and strips any %% trailing "**" and trailing "\n", returning the string as a list. normalize_otp_version(Vsn) -> %% It's fine to rely on the binary module here because we can %% be sure that it's available when the otp_release string does %% not begin with $R. Size = byte_size(Vsn), %% The shortest vsn string consists of at least two digits %% followed by "\n". Therefore, it's safe to assume Size >= 3. case binary:part(Vsn, {Size, -3}) of <<"**\n">> -> %% The OTP documentation mentions that a system patched %% using the otp_patch_apply tool available to licensed %% customers will leave a '**' suffix in the version as a %% flag saying the system consists of application versions %% from multiple OTP versions. We ignore this flag and %% drop the suffix, given for all intents and purposes, we %% cannot obtain relevant information from it as far as %% tooling is concerned. binary:bin_to_list(Vsn, {0, Size - 3}); _ -> binary:bin_to_list(Vsn, {0, Size - 1}) end. 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. get_experimental_3(Get, Config, Opt, Default) -> Val = Get(Config, Opt, Default), case Val of Default -> Default; Val -> ?CONSOLE("NOTICE: Using experimental option '~p'~n", [Opt]), Val 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} -> Cmd1 = "cmd /q /c " ++ lists:foldl(fun({Key, Value}, Acc) -> expand_env_variable(Acc, Key, Value) end, Cmd, Env), %% Remove left-over vars re:replace(Cmd1, "\\\$\\w+|\\\${\\w+}", "", [global, {return, list}]); _ -> 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("sh(~s)~n" "failed with return code ~w and the following 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_1(Vcs, Dir) -> case vcs_vsn_cmd(Vcs) of {plain, VsnString} -> VsnString; {cmd, CmdString} -> vcs_vsn_invoke(CmdString, Dir); unknown -> ?ABORT("vcs_vsn: Unknown vsn format: ~p\n", [Vcs]); 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. vcs_vsn_cmd(git) -> "git describe --always --tags"; vcs_vsn_cmd(p4) -> "echo #head"; vcs_vsn_cmd(hg) -> "hg identify -i"; vcs_vsn_cmd(bzr) -> "bzr revno"; vcs_vsn_cmd(svn) -> "svnversion"; vcs_vsn_cmd(fossil) -> "fossil info"; vcs_vsn_cmd({cmd, _Cmd}=Custom) -> Custom; vcs_vsn_cmd(Version) when is_list(Version) -> {plain, Version}; vcs_vsn_cmd(_) -> unknown. vcs_vsn_invoke(Cmd, Dir) -> {ok, VsnString} = rebar_utils:sh(Cmd, [{cd, Dir}, {use_stdout, false}]), string:strip(VsnString, right, $\n). %% %% Filter a list of erl_opts platform_define options such that only %% those which match the provided architecture regex are returned. %% 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]). cleanup_code_path(OrigPath) -> CurrentPath = code:get_path(), AddedPaths = CurrentPath -- OrigPath, %% If someone has removed paths, it's hard to get them back into %% the right order, but since this is currently rare, we can just %% fall back to code:set_path/1. case CurrentPath -- AddedPaths of OrigPath -> _ = [code:del_path(Path) || Path <- AddedPaths], true; _ -> code:set_path(OrigPath) end. rebar-2.6.0/src/rebar_xref.erl000066400000000000000000000252501254103774400162370ustar00rootroot00000000000000%% -*- 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]). %% for internal use only -export([info/2]). %% =================================================================== %% Public API %% =================================================================== xref(Config, _) -> %% Spin up xref {ok, _} = xref:start(xref), ok = xref:set_library_path(xref, code_path(Config)), xref:set_default(xref, [{warnings, rebar_config:get(Config, xref_warnings, false)}, {verbose, rebar_log:is_verbose(Config)}]), {ok, _} = xref:add_directory(xref, "ebin"), %% Save the code path prior to doing anything OrigPath = code:get_path(), true = code:add_path(rebar_utils:ebin_dir()), %% Get list of xref checks we want to run ConfXrefChecks = rebar_config:get(Config, xref_checks, [exports_not_used, undefined_function_calls]), SupportedXrefs = [undefined_function_calls, undefined_functions, locals_not_used, exports_not_used, deprecated_function_calls, deprecated_functions], XrefChecks = sets:to_list(sets:intersection( sets:from_list(SupportedXrefs), sets:from_list(ConfXrefChecks))), %% Run xref checks XrefNoWarn = xref_checks(XrefChecks), %% Run custom queries QueryChecks = rebar_config:get(Config, xref_queries, []), QueryNoWarn = lists:all(fun check_query/1, QueryChecks), %% Restore the original code path true = rebar_utils:cleanup_code_path(OrigPath), %% Stop xref stopped = xref:stop(xref), case lists:member(false, [XrefNoWarn, QueryNoWarn]) of true -> case rebar_config:get_xconf(Config, keep_going, false) of false -> ?FAIL; true -> ok end; false -> ok end. %% =================================================================== %% Internal functions %% =================================================================== info(help, xref) -> ?CONSOLE( "Run cross reference analysis.~n" "~n" "Valid rebar.config options:~n" " ~p~n" " ~p~n" " ~p~n" " ~p~n", [ {xref_warnings, false}, {xref_extra_paths,[]}, {xref_checks, [undefined_function_calls, undefined_functions, locals_not_used, exports_not_used, deprecated_function_calls, deprecated_functions]}, {xref_queries, [{"(xc - uc) || (xu - x - b" " - (\"mod\":\".*foo\"/\"4\"))",[]}]} ]). xref_checks(XrefChecks) -> XrefWarnCount = lists:foldl(fun run_xref_check/2, 0, XrefChecks), XrefWarnCount =:= 0. run_xref_check(XrefCheck, Acc) -> {ok, Results} = xref:analyze(xref, XrefCheck), FilteredResults =filter_xref_results(XrefCheck, Results), lists:foreach(fun(Res) -> display_xref_result(XrefCheck, Res) end, FilteredResults), Acc + length(FilteredResults). 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(Config) -> %% Slight hack to ensure that sub_dirs get properly included %% in code path for xref -- otherwise one gets a lot of undefined %% functions, even though those functions are present as part %% of compilation. H/t to @dluna. Long term we should tie more %% properly into the overall compile code path if possible. BaseDir = rebar_utils:base_dir(Config), [P || P <- code:get_path() ++ rebar_config:get(Config, xref_extra_paths, []) ++ [filename:join(BaseDir, filename:join(SubDir, "ebin")) || SubDir <- rebar_config:get(Config, sub_dirs, [])], filelib:is_dir(P)]. %% %% Ignore behaviour functions, and explicitly marked functions %% %% Functions can be ignored by using %% -ignore_xref([{F, A}, {M, F, A}...]). get_xref_ignorelist(Mod, XrefCheck) -> %% Get ignore_xref attribute and combine them in one list Attributes = try Mod:module_info(attributes) catch _Class:_Error -> [] end, IgnoreXref = keyall(ignore_xref, Attributes), BehaviourCallbacks = get_behaviour_callbacks(XrefCheck, Attributes), %% And create a flat {M,F,A} list lists:foldl( fun({F, A}, Acc) -> [{Mod,F,A} | Acc]; ({M, F, A}, Acc) -> [{M,F,A} | Acc] end, [], lists:flatten([IgnoreXref, BehaviourCallbacks])). keyall(Key, List) -> lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List). get_behaviour_callbacks(exports_not_used, Attributes) -> [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes) ++ keyall(behavior, Attributes)]; get_behaviour_callbacks(_XrefCheck, _Attributes) -> []. parse_xref_result({_, MFAt}) -> MFAt; parse_xref_result(MFAt) -> MFAt. filter_xref_results(XrefCheck, XrefResults) -> SearchModules = lists:usort( lists:map( fun({Mt,_Ft,_At}) -> Mt; ({{Ms,_Fs,_As},{_Mt,_Ft,_At}}) -> Ms; (_) -> undefined end, XrefResults)), Ignores = lists:flatmap(fun(Module) -> get_xref_ignorelist(Module, XrefCheck) end, SearchModules), [Result || Result <- XrefResults, not lists:member(parse_xref_result(Result), Ignores)]. display_xref_result(Type, XrefResult) -> { Source, SMFA, TMFA } = case XrefResult of {MFASource, MFATarget} -> {format_mfa_source(MFASource), format_mfa(MFASource), format_mfa(MFATarget)}; MFATarget -> {format_mfa_source(MFATarget), format_mfa(MFATarget), undefined} end, case Type of undefined_function_calls -> ?CONSOLE("~sWarning: ~s calls undefined function ~s (Xref)\n", [Source, SMFA, TMFA]); undefined_functions -> ?CONSOLE("~sWarning: ~s is undefined function (Xref)\n", [Source, SMFA]); locals_not_used -> ?CONSOLE("~sWarning: ~s is unused local function (Xref)\n", [Source, SMFA]); exports_not_used -> ?CONSOLE("~sWarning: ~s is unused export (Xref)\n", [Source, SMFA]); deprecated_function_calls -> ?CONSOLE("~sWarning: ~s calls deprecated function ~s (Xref)\n", [Source, SMFA, TMFA]); deprecated_functions -> ?CONSOLE("~sWarning: ~s is deprecated function (Xref)\n", [Source, SMFA]); Other -> ?CONSOLE("~sWarning: ~s - ~s xref check: ~s (Xref)\n", [Source, SMFA, TMFA, Other]) end. format_mfa({M, F, A}) -> ?FMT("~s:~s/~w", [M, F, A]). format_mfa_source(MFA) -> case find_mfa_source(MFA) of {module_not_found, function_not_found} -> ""; {Source, function_not_found} -> ?FMT("~s: ", [Source]); {Source, Line} -> ?FMT("~s:~w: ", [Source, Line]) end. %% %% 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}) -> case code:get_object_code(M) of error -> {module_not_found, function_not_found}; {M, Bin, _} -> find_function_source(M,F,A,Bin) end. find_function_source(M, F, A, Bin) -> AbstractCode = beam_lib:chunks(Bin, [abstract_code]), {ok, {M, [{abstract_code, {raw_abstract_v1, Code}}]}} = AbstractCode, %% Extract the original source filename from the abstract code [{attribute, 1, file, {Source, _}} | _] = Code, %% 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.6.0/src/rmemo.erl000066400000000000000000000201541254103774400152350ustar00rootroot00000000000000%%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %%% ex: ft=erlang ts=4 sw=4 et %%% %%%------------------------------------------------------------------- %%% @author Tuncer Ayaz %%% @copyright 2015, Tuncer Ayaz %%% @doc %%% memoization server %%% @end %%%------------------------------------------------------------------- %%% %%% Copyright (c) 2015 Tuncer Ayaz %%% %%% Permission to use, copy, modify, and/or distribute this software %%% for any purpose with or without fee is hereby granted, provided %%% that the above copyright notice and this permission notice appear %%% in all copies. %%% %%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL %%% WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED %%% WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE %%% AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR %%% CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM %%% LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, %%% NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN %%% CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. %% rebar-specific modifications: %% 1. rename to rmemo.erl %% 2. add support for R13 (see ets_tab/0) -module(rmemo). -behaviour(gen_server). %% API -export( [ start/0, start_link/0, stop/0, call/2, call/3 ]). %% gen_server callbacks -export( [ init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3 ]). -define(SERVER, ?MODULE). -define(TABLE, ?MODULE). -record(state, { ets_tab :: ets:tab() }). %%%=================================================================== %%% API %%%=================================================================== %%-------------------------------------------------------------------- %% @doc %% Start the server %% @end %%-------------------------------------------------------------------- -type reason() :: term(). -type error() :: {error, reason()}. -type start_res() :: {ok, pid()} | 'ignore' | error(). -spec start() -> start_res(). start() -> gen_server:start({local, ?SERVER}, ?MODULE, [], []). %%-------------------------------------------------------------------- %% @doc %% Start the server %% @end %%-------------------------------------------------------------------- -type start_link_res() :: start_res(). -spec start_link() -> start_link_res(). start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %%-------------------------------------------------------------------- %% @doc %% Stop the server %% @end %%-------------------------------------------------------------------- stop() -> gen_server:cast(?SERVER, stop). %%-------------------------------------------------------------------- %% @doc %% Call function and memoize result %% %% Instead of %% %% Res = Fun(A1, A2, [List1]) %% %% you call %% %% Res = memo:call(Fun, [A1, A2, [List1]]) %% %% or instead of %% %% %% Res = mod:expensive_function(A1, A2, [List1]) %% %% %% you call %% %% %% Res = memo:call(fun mod:expensive_function/3, [A1, A2, [List1]]) %% %% %% and any subsequent call will fetch the cached result and avoid the %% computation. %% %% This is of course only useful for expensive computations that are %% known to produce the same result given same arguments. It's worth %% mentioning that your call should be side-effect free, as naturally %% those won't be replayed. %% %% @end %%-------------------------------------------------------------------- -type fun_args() :: list(). -spec call(fun(), fun_args()) -> term(). call(F, A) -> call_1({F, A}). %%-------------------------------------------------------------------- %% @doc %% Call function and memoize result %% %% Instead of %% %% Res = mod:expensive_function(A1, A2, [List1]) %% %% you call %% %% Res = memo:call(mod, expensive_function, [A1, A2, [List1]]) %% %% and any subsequent call will fetch the cached result and avoid the %% computation. %% %% This is of course only useful for expensive computations that are %% known to produce the same result given same arguments. It's worth %% mentioning that your call should be side-effect free, as naturally %% those won't be replayed.%% %% %% @end %%-------------------------------------------------------------------- %% fun() is not just the name of a fun, so we define an alias for %% atom() for call(M, F, A). -type fun_name() :: atom(). -spec call(module(), fun_name(), fun_args()) -> term(). call(M, F, A) when is_list(A) -> call_1({M, F, A}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %%-------------------------------------------------------------------- %% @private %% @doc %% Initialize the server %% @end %%-------------------------------------------------------------------- init(_) -> {ok, #state{ ets_tab = ets_tab() } }. -spec ets_tab() -> ets:tab(). ets_tab() -> ErtsApp = filename:join(code:lib_dir(erts, ebin), "erts.app"), Concurrency = %% If erts.app exists, we run on at least R14. That means we %% can use ets read_concurrency. %% TODO: Remove and revert to vanilla memo.erl from %% https://github.com/tuncer/memo once we require at least %% R14B and drop support for R13. case filelib:is_regular(ErtsApp) of true -> [{read_concurrency, true}]; false -> [] end, ets:new( ?TABLE, [ named_table, protected, set ] ++ Concurrency ). %%-------------------------------------------------------------------- %% @private %% @doc %% Handle call messages %% @end %%-------------------------------------------------------------------- handle_call({save, Key, Res}, _From, State) -> {reply, save(Key, Res), State}; handle_call(_Request, _From, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handle cast messages %% @end %%-------------------------------------------------------------------- handle_cast(stop, State) -> {stop, normal, State}; handle_cast(_Msg, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% Handle all non call/cast messages %% @end %%-------------------------------------------------------------------- handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% @private %% @doc %% This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_server terminates %% with Reason. The return value is ignored. %% @end %%-------------------------------------------------------------------- terminate(_Reason, _State) -> ok. %%-------------------------------------------------------------------- %% @private %% @doc %% Convert process state when code is changed %% @end %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%=================================================================== %%% Internal functions %%%=================================================================== -type call() :: {module(), fun_name(), fun_args()} | {fun(), fun_args()}. -spec call_1(call()) -> term(). call_1(Call) -> Key = key(Call), case ets:lookup(?TABLE, Key) of [] -> Res = apply(Call), true = gen_server:call(?SERVER, {save, Key, Res}, infinity), Res; [{Key, Mem}] -> Mem end. -type key_args() :: call(). -type key() :: non_neg_integer(). -spec key(key_args()) -> key(). key(Call) -> erlang:phash2(Call). -spec apply(call()) -> term(). apply({F, A}) -> erlang:apply(F, A); apply({M, F, A}) -> erlang:apply(M, F, A). -type val() :: term(). -spec save(key(), val()) -> true. save(K, V) -> ets:insert(?TABLE, {K, V}). rebar-2.6.0/test/000077500000000000000000000000001254103774400136005ustar00rootroot00000000000000rebar-2.6.0/test/rebar_compiler_tests.erl000066400000000000000000000144141254103774400205170ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Vlad Dumitrescu (vladdu55@gmail.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 Vlad Dumitrescu %% @author Chris Bernard %% @doc This tests functionality provided by the rebar command 'compile'. %% ------------------------------------------------------------------- -module(rebar_compiler_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 compiler tests %% ==================================================================== keep_going_test_() -> {"Ensure compiler keeps going after finding errors, when --keep-going is requested", setup, fun() -> setup_basic_project(), rebar("-k compile") end, fun teardown/1, fun(RebarOut)-> [ {"Continue after error", ?_assert(string:str(RebarOut, "Continuing after build error") =/= 0)}, ?_assert(string:str(RebarOut, "ERROR: compile failed") == 0) ] ++ assert_files_in("ebin", ["ebin/myapp_mymod2.beam"]) end }. keep_going_firstfiles_test_() -> {"Ensure compiler keeps going after finding errors in erl_first_files, when --keep-going is requested", setup, fun() -> setup_basic_project(), setup_rebar_config(), rebar("-k compile") end, fun teardown/1, fun(RebarOut)-> [ {"Continue after error", ?_assert(string:str(RebarOut, "Continuing after build error") =/= 0)}, ?_assert(string:str(RebarOut, "ERROR: compile failed") == 0) ] ++ assert_files_in("ebin", ["ebin/myapp_mymod2.beam"]) end }. not_keep_going_test_() -> {"Ensure compiler stops after finding errors", setup, fun() -> setup_basic_project(), setup_rebar_config(), rebar("compile") end, fun teardown/1, fun(RebarOut)-> [ {"Exit after error", ?_assert(string:str(RebarOut, "ERROR: compile failed") =/= 0)} ] ++ assert_files_not_in("ebin", ["ebin/myapp_mymod2.beam"]) end }. %% ==================================================================== %% Setup and Teardown %% ==================================================================== -define(rebar_config, ["{erl_first_files, [\"src/myapp_mymod1.erl\"]}." ]). %% this file has a syntax error -define(myapp_mymod1, ["-module(myapp_mymod1).\n", "-export([myfunc/0]).\n", "my func() -> [4], ok.\n"]). -define(myapp_mymod2, ["-module(myapp_mymod2).\n", "-export([myfunc/0]).\n", "myfunc() -> [4], ok.\n"]). make_tmp_dir() -> case file:make_dir(?TMP_DIR) of ok -> ok; {error, eexist} -> remove_tmp_dir(), make_tmp_dir(); Error -> throw(Error) end. setup_environment() -> ok = make_tmp_dir(), prepare_rebar_script(), ok = file:set_cwd(?TMP_DIR). setup_rebar_config() -> ok = file:write_file("rebar.config", ?rebar_config). setup_basic_project() -> setup_environment(), rebar("create-app appid=myapp"), ok = file:make_dir("ebin"), ok = file:write_file("src/myapp_mymod1.erl", ?myapp_mymod1), ok = file:write_file("src/myapp_mymod2.erl", ?myapp_mymod2). 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 ++ ".cmd", ?TMP_DIR ++ "rebar.cmd") 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(_, []) -> []. rebar-2.6.0/test/rebar_eunit_tests.erl000066400000000000000000000510761254103774400200360ustar00rootroot00000000000000%% -*- 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}. eunit_with_suites_and_tests_test_() -> [{"Ensure EUnit runs selected suites", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod2") end, fun teardown/1, fun(RebarOut) -> [{"Selected suite tests in 'test' directory are found and run", ?_assert(string:str(RebarOut, "myapp_mymod2_tests:") =/= 0)}, {"Selected suite tests in 'src' directory are found and run", ?_assert(string:str(RebarOut, "myapp_mymod2:") =/= 0)}, {"Unselected suite tests in 'test' directory are not run", ?_assert(string:str(RebarOut, "myapp_mymod_tests:") =:= 0)}, {"Unselected suite tests in 'src' directory are not run", ?_assert(string:str(RebarOut, "myapp_mymod:") =:= 0)}, {"Selected suite tests are only run once", ?_assert(string:str(RebarOut, "All 4 tests passed") =/= 0)}] end}, {"Ensure EUnit runs selected _tests suites", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod2_tests") end, fun teardown/1, fun(RebarOut) -> [{"Selected suite tests in 'test' directory are found and run", ?_assert(string:str(RebarOut, "myapp_mymod2_tests:") =/= 0)}, {"Selected suite tests in 'src' directory are not run", ?_assert(string:str(RebarOut, "myapp_mymod2:") =:= 0)}, {"Unselected suite tests in 'test' directory are not run", ?_assert(string:str(RebarOut, "myapp_mymod_tests:") =:= 0)}, {"Unselected suite tests in 'src' directory are not run", ?_assert(string:str(RebarOut, "myapp_mymod:") =:= 0)}, {"Selected suite tests are only run once", ?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}] end}, {"Ensure EUnit runs a specific test defined in a selected suite", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod2 tests=myprivate2") end, fun teardown/1, fun(RebarOut) -> [{"Selected suite tests are found and run", ?_assert(string:str(RebarOut, "myapp_mymod2:myprivate2_test/0") =/= 0)}, {"Selected suite tests is run once", ?_assert(string:str(RebarOut, "Test passed") =/= 0)}] end}, {"Ensure EUnit runs a specific generator test defined in a selected suite", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod3 tests=mygenerator") end, fun teardown/1, fun(RebarOut) -> [{"Selected suite's generator test is found and run", ?_assert(string:str(RebarOut, "myapp_mymod3:mygenerator_test_/0") =/= 0)}, {"Selected suite's generator test raises an error", ?_assert(string:str(RebarOut, "assertEqual_failed") =/= 0)}, {"Selected suite tests is run once", ?_assert(string:str(RebarOut, "Failed: 1.") =/= 0)}] end}, {"Ensure EUnit runs specific tests defined in selected suites", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod,myapp_mymod2" " tests=myprivate,myfunc2") end, fun teardown/1, fun(RebarOut) -> [{"Selected suite tests are found and run", [?_assert(string:str(RebarOut, "myapp_mymod:myprivate_test/0") =/= 0), ?_assert(string:str(RebarOut, "myapp_mymod2:myprivate2_test/0") =/= 0), ?_assert( string:str(RebarOut, "myapp_mymod2_tests:myfunc2_test/0") =/= 0)]}, {"Selected suite tests are run once", ?_assert(string:str(RebarOut, "All 3 tests passed") =/= 0)}] end}, {"Ensure EUnit runs specific test in a _tests suite", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod2_tests tests=common_name_test") end, fun teardown/1, fun(RebarOut) -> [{"Only selected suite tests are found and run", [?_assert(string:str(RebarOut, "myapp_mymod2:common_name_test/0") =:= 0), ?_assert(string:str(RebarOut, "myapp_mymod2_tests:common_name_test/0") =/= 0)]}, {"Selected suite tests is run once", ?_assert(string:str(RebarOut, "Test passed") =/= 0)}] end}, {"Ensure EUnit runs a specific test without a specified suite", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit tests=myprivate") end, fun teardown/1, fun(RebarOut) -> [{"Only selected suite tests are found and run", [?_assert(string:str(RebarOut, "myapp_mymod:myprivate_test/0") =/= 0), ?_assert(string:str(RebarOut, "myapp_mymod2:myprivate2_test/0") =/= 0)]}, {"Selected suite tests is run once", ?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}] end}, {"Ensure EUnit runs a specific test by qualified function name", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit tests=myapp_mymod:myprivate_test") end, fun teardown/1, fun(RebarOut) -> [{"Selected test is run", [?_assert(string:str(RebarOut, "myapp_mymod:myprivate_test/0") =/= 0)]}, {"Only selected test is run", [?_assert(string:str(RebarOut, "Test passed.") =/= 0)]}] end}, {"Ensure EUnit runs a specific test by qualified function " ++ "name and tests from other module", setup, fun() -> setup_project_with_multiple_modules(), rebar("-v eunit suites=myapp_mymod3 " ++ "tests=myapp_mymod:myprivate_test") end, fun teardown/1, fun(RebarOut) -> [{"Selected test is run", [?_assert(string:str(RebarOut, "myapp_mymod:myprivate_test/0") =/= 0)]}, {"Tests from module are run", [?_assert(string:str(RebarOut, "myapp_mymod3:") =/= 0)]}, {"Only selected tests are run", [?_assert(string:str(RebarOut, "Failed: 1. Skipped: 0. Passed: 1") =/= 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, fun(RebarOut) -> [{"Error messages are not present", ?_assert(string:str(RebarOut, "Cover analyze failed for") =:= 0)}, {"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"])}] end}. 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 suites=mysuite") end, fun teardown/1, fun(RebarOut) -> [{"Error messages are not present", ?_assert(string:str(RebarOut, "Cover analyze failed for") =:= 0)}, {"Cover reports are generated for module", assert_files_in("the temporary eunit directory", [".eunit/index.html", ".eunit/mysuite.COVER.html"])}, {"Only production modules get coverage reports", assert_files_not_in("the temporary eunit directory", [".eunit/myapp_app.COVER.html", ".eunit/myapp_mymod.COVER.html", ".eunit/myapp_sup.COVER.html", ".eunit/myapp_mymod_tests.COVER.html"])}] end}. 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"])}. code_path_test_() -> [{"Ensuring that fast code path cleanup is correct for adds", setup, fun make_tmp_dir/0, fun(_) -> remove_tmp_dir() end, fun() -> OPath = code:get_path(), PathZ = ?TMP_DIR ++ "some_path", PathA = ?TMP_DIR ++ "some_other_path", ok = file:make_dir(PathZ), ok = file:make_dir(PathA), true = code:add_pathz(PathZ), true = code:add_patha(PathA), %% make sure that they've been added ?assertEqual([PathA] ++ OPath ++ [PathZ], code:get_path()), true = rebar_utils:cleanup_code_path(OPath), ?assertEqual(OPath, code:get_path()) end}, {"Ensuring that fast code path cleanup is correct for removes", setup, fun make_tmp_dir/0, fun(_) -> remove_tmp_dir() end, fun() -> OPath = code:get_path(), Path1 = lists:nth(10, OPath), Path2 = lists:nth(11, OPath), true = code:del_path(Path1), true = code:del_path(Path2), %% make sure that they've been added ?assertEqual(OPath -- [Path1, Path2], code:get_path()), true = rebar_utils:cleanup_code_path(OPath), ?assertEqual(OPath, code:get_path()) end}, {"Ensuring that fast code path cleanup is equivalent for adds", setup, fun make_tmp_dir/0, fun(_) -> remove_tmp_dir() end, fun() -> OPath = code:get_path(), PathZ = ?TMP_DIR ++ "some_path", PathA = ?TMP_DIR ++ "some_other_path", ok = file:make_dir(PathZ), ok = file:make_dir(PathA), true = code:add_pathz(PathZ), true = code:add_patha(PathA), %% make sure that they've been added ?assertEqual([PathA] ++ OPath ++ [PathZ], code:get_path()), true = rebar_utils:cleanup_code_path(OPath), CleanedPath = code:get_path(), true = code:add_pathz(PathZ), true = code:add_patha(PathA), true = code:set_path(OPath), ?assertEqual(CleanedPath, code:get_path()) end}]. %% ==================================================================== %% 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(myapp_mymod2, ["-module(myapp_mymod2).\n", "-export([myfunc2/0]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "myfunc2() -> ok.\n", "myprivate2_test() -> ?assert(true).\n", "common_name_test() -> ?assert(true).\n"]). -define(myapp_mymod2_tests, ["-module(myapp_mymod2_tests).\n", "-compile([export_all]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "myfunc2_test() -> ?assertMatch(ok, myapp_mymod2:myfunc2()).\n", "common_name_test() -> ?assert(true).\n"]). -define(myapp_mymod3, ["-module(myapp_mymod3).\n", "-export([myfunc3/0]).\n", "-include_lib(\"eunit/include/eunit.hrl\").\n", "myfunc3() -> ok.\n", "mygenerator_test_() -> [?_assertEqual(true, false)].\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() -> case file:make_dir(?TMP_DIR) of ok -> ok; {error, eexist} -> remove_tmp_dir(), make_tmp_dir(); Error -> throw(Error) end. 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_project_with_multiple_modules() -> setup_basic_project(), ok = file:write_file("test/myapp_mymod2_tests.erl", ?myapp_mymod2_tests), ok = file:write_file("src/myapp_mymod2.erl", ?myapp_mymod2), ok = file:write_file("src/myapp_mymod3.erl", ?myapp_mymod3). 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 ++ ".cmd", ?TMP_DIR ++ "rebar.cmd") 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], ?assert(length(Result) =:= 1) end. rebar-2.6.0/test/rebar_file_utils_tests.erl000066400000000000000000000237651254103774400210550ustar00rootroot00000000000000%% -*- 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(SRC, "source dir?"). -define(DST, "dest (dir)"). -define(FILE1, "file 1"). -define(FILE2, "file(2)"). -define(DIR_TREE, [{d,?SRC,[{f,?FILE1}, {f,?FILE2}]}, {d,?DST,[]}]). -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(?SRC, 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,?SRC,"file*"])) end, fun teardown/1, [assert_files_not_in(?SRC, file_list())]}. rm_rf_dir_test_() -> {"rm_rf removes directory tree", setup, fun() -> setup(), rebar_file_utils:rm_rf(filename:join([?TMP_DIR,?SRC])) end, fun teardown/1, [?_assertNot(filelib:is_dir(filename:join([?TMP_DIR,?SRC])))]}. %% ==================================================================== %% 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,?SRC,?FILE1])], filename:join([?TMP_DIR,?DST,"new_file"])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,?DST,"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,?SRC,?FILE1])], filename:join([?TMP_DIR,?DST])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,?DST,?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,?SRC])], filename:join([?TMP_DIR,?DST])) end, fun teardown/1, [?_assert(filelib:is_dir(filename:join([?TMP_DIR,?DST,?SRC]))), assert_files_in("dest/source", [filename:join([?TMP_DIR,?DST,?SRC,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,?SRC,"*1"])], filename:join([?TMP_DIR,?DST])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,?DST,?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,?DST])) end, fun teardown/1, [?_assert(filelib:is_dir(filename:join([?TMP_DIR,?DST,?SRC]))), assert_files_in("dest/source", [filename:join([?TMP_DIR,?DST,?SRC,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,?DST,?FILE1]), <<"test">>), rebar_file_utils:cp_r([filename:join([?TMP_DIR,?SRC,?FILE1])], filename:join([?TMP_DIR,?DST])) end, fun teardown/1, [?_assertMatch({ok,?FILE_CONTENT}, file:read_file( filename:join([?TMP_DIR,?DST,?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,?DST,?SRC])), ok = file:write_file( filename:join([?TMP_DIR,?DST,?SRC,?FILE1]), <<"test">>), rebar_file_utils:cp_r([filename:join([?TMP_DIR,?SRC])], filename:join([?TMP_DIR,?DST])) end, fun teardown/1, [?_assertMatch({ok,?FILE_CONTENT}, file:read_file( filename:join([?TMP_DIR,?DST,?SRC,?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,?DST,?FILE1]),<<"test">>), ok = file:change_mode( filename:join([?TMP_DIR,?DST,?FILE1]),0) end, fun teardown/1, [?_assertThrow(rebar_abort, rebar_file_utils:cp_r( [filename:join([?TMP_DIR,?SRC,?FILE1])], filename:join([?TMP_DIR,?DST])))]}. 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,?DST,?SRC])), ok = file:write_file( filename:join([?TMP_DIR,?DST,?SRC,?FILE1]), <<"test">>), ok = file:change_mode( filename:join([?TMP_DIR,?DST,?SRC,?FILE1]),0) end, fun teardown/1, [?_assertThrow(rebar_abort, rebar_file_utils:cp_r( [filename:join([?TMP_DIR,?SRC])], filename:join([?TMP_DIR,?DST])))]}. mv_file_test_() -> {"move a file to folder", setup, fun() -> setup(), rebar_file_utils:mv(filename:join([?TMP_DIR,?SRC,?FILE1]), filename:join([?TMP_DIR,?DST])) end, fun teardown/1, [?_assert(filelib:is_regular(filename:join([?TMP_DIR,?DST,?FILE1]))), ?_assertNot(filelib:is_regular( filename:join([?TMP_DIR,?SRC,?FILE1])))]}. %% ==================================================================== %% Utilities %% ==================================================================== file_list() -> [filename:join([?TMP_DIR,?SRC,F]) || F <- [?FILE1,?FILE2]]. %% ==================================================================== %% Setup and Teardown %% ==================================================================== setup() -> make_dir_tree(?TMP_DIR,?DIR_TREE). make_dir_tree(Parent, [{d,Dir,Contents} | Rest]) -> NewDir = filename:join(Parent,Dir), ok = filelib:ensure_dir(filename:join(NewDir, "tmp")), 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.6.0/test/rebar_otp_release_tests.erl000066400000000000000000000040271254103774400212060ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2014 Tuncer Ayaz %% %% 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_release_tests). -include_lib("eunit/include/eunit.hrl"). check_otp_release_test() -> case rebar_utils:otp_release() of %% <= R16 [$R,N|_] when is_integer(N) -> ?assert(true); %% >= 17.x [N|_]=Rel when is_integer(N) -> %% Check that it has at least Major.Minor. ?assert(length(string:tokens(Rel, ".")) > 1), %% If otp_patch_apply was used and the release version has %% a "**" suffix, we drop that part in otp_release/0. ?assertEqual(0, string:str(Rel, "*")), %% Check that "\n" is dropped in otp_release/0. ?assertEqual(0, string:str(Rel, "\n")) end. rebar-2.6.0/test/rebar_require_vsn_tests.erl000066400000000000000000000020311254103774400212370ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(rebar_require_vsn_tests). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). version_tuple_test_() -> [%% typical cases ?_assert(check("R15B", "eunit") =:= {15, 0}), ?_assert(check("R15B01", "eunit") =:= {15, 1}), ?_assert(check("R15B02", "eunit") =:= {15, 2}), ?_assert(check("R15B03-1", "eunit") =:= {15, 3}), ?_assert(check("R15B03", "eunit") =:= {15, 3}), ?_assert(check("R16B", "eunit") =:= {16, 0}), ?_assert(check("R16B01", "eunit") =:= {16, 1}), ?_assert(check("R16B02", "eunit") =:= {16, 2}), ?_assert(check("R16B03", "eunit") =:= {16, 3}), ?_assert(check("R16B03-1", "eunit") =:= {16, 3}), ?_assert(check("17", "eunit") =:= {17, 0}), %% error cases ?_assertException(throw, rebar_abort, check("", "eunit")), ?_assertException(throw, rebar_abort, check("abc", "eunit")) ]. check(OtpRelease, Type) -> rebar_require_vsn:version_tuple(abort, OtpRelease, Type). rebar-2.6.0/test/rebar_utils_tests.erl000066400000000000000000000042401254103774400200410ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% %% rebar: Erlang Build Tools %% %% Copyright (c) 2015 Mark Anderson %% %% 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 Mark Anderson %% @doc Tests functionality of rebar_utils module. %% @copyright 2015 Mark Anderson %% ------------------------------------------------------------------- -module(rebar_utils_tests). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). expand_env_variable_test_() -> [{"Ensure that variable expansion handles nonwhitespace", fun() -> Test = "$V $V/ $Vw", ?assertEqual("Val Val/ $Vw", rebar_utils:expand_env_variable(Test, "V", "Val") ) end}, {"Ensure that variable expansion with braces works", fun() -> Test = "${V} ${V}/ ${V}w ${Vw}", ?assertEqual("Val Val/ Valw ${Vw}", rebar_utils:expand_env_variable(Test, "V", "Val") ) end}]. rebar-2.6.0/test/rebar_xref_eunit.erl000066400000000000000000000224771254103774400176430ustar00rootroot00000000000000%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et -module(rebar_xref_eunit). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). -define(REBAR_SCRIPT, "../rebar"). -define(TMP_DIR, "tmp_xref_eunit/"). xref_test_() -> {"Test the various xref warnings", setup, fun() -> setup_project(false), rebar("compile"), rebar("skip_deps=true xref") end, fun teardown/1, fun(RebarOut) -> [ {"Undefined function", ?_assert(string:str(RebarOut, "myapp_somemod:notavailable/1 is undefined function") =/= 0)}, {"Undefined function call", ?_assert(string:str(RebarOut, "myapp_othermod:somefunc/0 calls undefined function myapp_somemod:notavailable/1") =/= 0)}, {"Deprecated function", ?_assert(string:str(RebarOut, "myapp_mymod:fdeprecated/0 is deprecated function") =/= 0)}, {"Deprecated function call", ?_assert(string:str(RebarOut, "myapp_othermod:somefunc/0 calls deprecated function myapp_mymod:fdeprecated/0") =/= 0)}, {"Unused local", ?_assert(string:str(RebarOut, "myapp_mymod:localfunc2/0 is unused local function") =/= 0)}, {"Unused export 1", ?_assert(string:str(RebarOut, "myapp_behaviour1:behaviour_info/1 is unused export") =/= 0)}, {"Unused export 2", ?_assert(string:str(RebarOut, "myapp_behaviour2:behaviour_info/1 is unused export") =/= 0)}, {"Unused export 3", ?_assert(string:str(RebarOut, "myapp_mymod:other2/1 is unused export") =/= 0)}, {"Unused export 4", ?_assert(string:str(RebarOut, "myapp_othermod:somefunc/0 is unused export") =/= 0)}, {"Suppressed behaviour export 1", ?_assert(string:str(RebarOut, "myapp_mymod:bh1_a/1 is unused export") =:= 0)}, {"Suppressed behaviour export 2", ?_assert(string:str(RebarOut, "myapp_mymod:bh1_b/1 is unused export") =:= 0)}, {"Suppressed behaviour export 3", ?_assert(string:str(RebarOut, "myapp_mymod:bh2_a/1 is unused export") =:= 0)}, {"Suppressed behaviour export 4", ?_assert(string:str(RebarOut, "myapp_mymod:bh2_b/1 is unused export") =:= 0)} ] end}. xref_ignore_test_() -> {"Test the suppression of xref warnings", setup, fun() -> setup_project(ignore_xref), rebar("compile"), rebar("skip_deps=true xref") end, fun teardown/1, fun(RebarOut) -> [ {"Undefined function can not be suppressed.", ?_assert(string:str(RebarOut, "myapp_somemod:notavailable/1 is undefined function") =/= 0)}, {"Supppressed undefined function call", ?_assert(string:str(RebarOut, "myapp_othermod:somefunc/0 calls undefined function myapp_somemod:notavailable/1") =:= 0)}, {"Supppressed deprecated function", ?_assert(string:str(RebarOut, "myapp_mymod:fdeprecated/0 is deprecated function") =:= 0)}, {"Supppressed deprecated function call", ?_assert(string:str(RebarOut, "myapp_othermod:somefunc/0 calls deprecated function myapp_mymod:fdeprecated/0") =:= 0)}, {"Supppressed unused local", ?_assert(string:str(RebarOut, "myapp_mymod:localfunc2/0 is unused local function") =:= 0)}, {"Supppressed unused export 1", ?_assert(string:str(RebarOut, "myapp_behaviour1:behaviour_info/1 is unused export") =:= 0)}, {"Supppressed unused export 2", ?_assert(string:str(RebarOut, "myapp_behaviour2:behaviour_info/1 is unused export") =:= 0)}, {"Supppressed unused export 3", ?_assert(string:str(RebarOut, "myapp_mymod:other2/1 is unused export") =:= 0)}, {"Supppressed unused export 4", ?_assert(string:str(RebarOut, "myapp_othermod:somefunc/0 is unused export") =:= 0)}, {"Suppressed behaviour export 1", ?_assert(string:str(RebarOut, "myapp_mymod:bh1_a/1 is unused export") =:= 0)}, {"Suppressed behaviour export 2", ?_assert(string:str(RebarOut, "myapp_mymod:bh1_b/1 is unused export") =:= 0)}, {"Suppressed behaviour export 3", ?_assert(string:str(RebarOut, "myapp_mymod:bh2_a/1 is unused export") =:= 0)}, {"Suppressed behaviour export 4", ?_assert(string:str(RebarOut, "myapp_mymod:bh2_b/1 is unused export") =:= 0)} ] end}. %% ==================================================================== %% Setup and Teardown %% ==================================================================== -define(myapp_behaviour1, ["-module(myapp_behaviour1).\n", "-export([behaviour_info/1]).\n"]). -define(myapp_behaviour1_body, ["behaviour_info(callbacks) -> [{bh1_a,1},{bh1_b,1}];\n", "behaviour_info(_Other) -> undefined.\n"]). -define(myapp_behaviour1_ignorexref, ["-ignore_xref({behaviour_info,1}).\n"]). -define(myapp_behaviour2, ["-module(myapp_behaviour2).\n", "-export([behaviour_info/1]).\n"]). -define(myapp_behaviour2_body, ["behaviour_info(callbacks) -> [{bh2_a,1},{bh2_b,1}];\n", "behaviour_info(_Other) -> undefined.\n"]). -define(myapp_behaviour2_ignorexref, ["-ignore_xref({behaviour_info,1}).\n"]). -define(myapp_mymod, ["-module(myapp_mymod).\n", "-export([bh1_a/1,bh1_b/1,bh2_a/1,bh2_b/1,other1/1,other2/1,fdeprecated/0]).\n", "-behaviour(myapp_behaviour1).\n", % 2 behaviours "-behaviour(myapp_behaviour2).\n", "-deprecated({fdeprecated,0}).\n"]). % deprecated function -define(myapp_mymod_body, ["bh1_a(A) -> localfunc1(bh1_a, A).\n", % behaviour functions "bh1_b(A) -> localfunc1(bh1_b, A).\n", "bh2_a(A) -> localfunc1(bh2_a, A).\n", "bh2_b(A) -> localfunc1(bh2_b, A).\n", "other1(A) -> localfunc1(other1, A).\n", % regular exported functions "other2(A) -> localfunc1(other2, A).\n", "localfunc1(A, B) -> {A, B}.\n", % used local "localfunc2() -> ok.\n", % unused local "fdeprecated() -> ok.\n" % deprecated function ]). -define(myapp_mymod_ignorexref, ["-ignore_xref([{other2,1},{localfunc2,0},{fdeprecated,0}]).\n"]). -define(myapp_othermod, ["-module(myapp_othermod).\n", "-export([somefunc/0]).\n"]). -define(myapp_othermod_body, ["somefunc() ->\n", " myapp_mymod:other1(arg),\n", " myapp_somemod:notavailable(arg),\n", " myapp_mymod:fdeprecated().\n" ]). -define(myapp_othermod_ignorexref, ["-ignore_xref([{myapp_somemod,notavailable,1},{somefunc,0}]).\n", "-ignore_xref({myapp_mymod,fdeprecated,0}).\n"]). -define(myapp_rebarconfig, ["{erl_opts, [debug_info]}.\n", "{xref_checks, [deprecated_function_calls,deprecated_functions,\n", " undefined_function_calls,undefined_functions,\n", " exports_not_used,locals_not_used]}.\n" ]). setup_environment() -> ok = file:make_dir(?TMP_DIR), prepare_rebar_script(), ok = file:set_cwd(?TMP_DIR). prepare_project() -> setup_environment(), rebar("create-app appid=myapp"), ok = file:make_dir("ebin"). setup_project(ignore_xref) -> prepare_project(), ok = file:write_file("src/myapp_behaviour1.erl", ?myapp_behaviour1 ++ ?myapp_behaviour1_ignorexref ++ ?myapp_behaviour1_body), ok = file:write_file("src/myapp_behaviour2.erl", ?myapp_behaviour2 ++ ?myapp_behaviour2_ignorexref++ ?myapp_behaviour2_body), ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod ++ ?myapp_mymod_ignorexref ++ ?myapp_mymod_body), ok = file:write_file("src/myapp_othermod.erl", ?myapp_othermod ++ ?myapp_othermod_ignorexref ++ ?myapp_othermod_body), ok = file:write_file("rebar.config", ?myapp_rebarconfig); setup_project(_) -> prepare_project(), ok = file:write_file("src/myapp_behaviour1.erl", ?myapp_behaviour1 ++ ?myapp_behaviour1_body), ok = file:write_file("src/myapp_behaviour2.erl", ?myapp_behaviour2 ++ ?myapp_behaviour2_body), ok = file:write_file("src/myapp_mymod.erl", ?myapp_mymod ++ ?myapp_mymod_body), ok = file:write_file("src/myapp_othermod.erl", ?myapp_othermod ++ ?myapp_othermod_body), ok = file:write_file("rebar.config", ?myapp_rebarconfig). teardown(_) -> ok = file:set_cwd(".."), ok = remove_tmp_dir(). 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. rebar-2.6.0/test/upgrade_project/000077500000000000000000000000001254103774400167555ustar00rootroot00000000000000rebar-2.6.0/test/upgrade_project/README.md000066400000000000000000000025251254103774400202400ustar00rootroot00000000000000#### Building version 0.1 rebar compile cd rel rebar generate mv dummy dummy_0.1 cd .. 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 cd rel 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 dummy_0.2.tar.gz mv dummy dummy_0.2 #### Deploying with release_handler mv dummy_0.2.tar.gz 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(). #### Building version 0.3 rm -r rel/dummy # Now repeat steps in 'Building version 0.2' and 'Deploying with release_handler' # while replacing '0.2' by '0.3' and '0.1' by '0.2'. rebar-2.6.0/test/upgrade_project/apps/000077500000000000000000000000001254103774400177205ustar00rootroot00000000000000rebar-2.6.0/test/upgrade_project/apps/dummy/000077500000000000000000000000001254103774400210535ustar00rootroot00000000000000rebar-2.6.0/test/upgrade_project/apps/dummy/src/000077500000000000000000000000001254103774400216425ustar00rootroot00000000000000rebar-2.6.0/test/upgrade_project/apps/dummy/src/dummy.app.src000066400000000000000000000003011254103774400242570ustar00rootroot00000000000000{application, dummy, [ {description, "a dummy app"}, {vsn, "0.1"}, {registered, [ dummy_app ]}, {mod, {dummy_app, []}}, {applications, [kernel, stdlib, sasl]} ]}. rebar-2.6.0/test/upgrade_project/apps/dummy/src/dummy_app.erl000066400000000000000000000002051254103774400243360ustar00rootroot00000000000000-module(dummy_app). -behaviour(application). -export([start/2, stop/1]). start(_,_) -> dummy_sup:start_link(). stop(_) -> ok. rebar-2.6.0/test/upgrade_project/apps/dummy/src/dummy_server.erl000066400000000000000000000022051254103774400250660ustar00rootroot00000000000000-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.6.0/test/upgrade_project/apps/dummy/src/dummy_sup.erl000066400000000000000000000005351254103774400243730ustar00rootroot00000000000000-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.6.0/test/upgrade_project/rebar.config000066400000000000000000000000551254103774400212370ustar00rootroot00000000000000{sub_dirs, [ "apps/dummy", "rel" ]}. rebar-2.6.0/test/upgrade_project/rel/000077500000000000000000000000001254103774400175375ustar00rootroot00000000000000rebar-2.6.0/test/upgrade_project/rel/files/000077500000000000000000000000001254103774400206415ustar00rootroot00000000000000rebar-2.6.0/test/upgrade_project/rel/files/dummy000077500000000000000000000226401254103774400217260ustar00rootroot00000000000000#!/bin/sh # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et # /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then POSIX_SHELL="true" export POSIX_SHELL # To support 'whoami' add /usr/ucb to path PATH=/usr/ucb:$PATH export PATH exec /usr/bin/ksh $0 "$@" fi # clear it so if we invoke other scripts, they run as ksh unset POSIX_SHELL RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd -P) CALLER_DIR=$PWD RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*} RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc # Note the trailing slash on $PIPE_DIR/ PIPE_DIR=/tmp/$RUNNER_BASE_DIR/ RUNNER_USER= WHOAMI=$(whoami) # Make sure this script is running as the appropriate user if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then type sudo > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "sudo doesn't appear to be installed and your EUID isn't $RUNNER_USER" 1>&2 exit 1 fi echo "Attempting to restart script through sudo -H -u $RUNNER_USER" >&2 exec sudo -H -u $RUNNER_USER -i $RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT $@ fi # 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 $CWD/vm.args if exists, otherwise releases/APP_VSN/vm.args, or # else etc/vm.args if [ -e "$CALLER_DIR/vm.args" ]; then VMARGS_PATH=$CALLER_DIR/vm.args USE_DIR=$CALLER_DIR else USE_DIR=$RUNNER_BASE_DIR 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 fi RUNNER_LOG_DIR=$USE_DIR/log # Make sure log directory exists mkdir -p $RUNNER_LOG_DIR # Use releases/VSN/sys.config if it exists otherwise use etc/app.config if [ -e "$USE_DIR/sys.config" ]; then CONFIG_PATH="$USE_DIR/sys.config" else 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 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 # Make sure CWD is set to the right dir cd $USE_DIR # Make sure log directory exists mkdir -p $USE_DIR/log # Add ERTS bin dir to our path ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin # 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" # Common functions # Ping node without allowing nodetool to take stdin ping_node() { $NODETOOL ping < /dev/null } # Set the PID global variable, return 1 on error get_pid() { PID=`$NODETOOL getpid < /dev/null` ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" return 1 fi # don't allow empty or init pid's if [ -z $PID ] || [ "$PID" -le 1 ]; then return 1 fi return 0 } # Check the first argument for instructions case "$1" in start|start_boot) # Make sure there is not already a node running RES=`ping_node` if [ "$RES" = "pong" ]; then echo "Node is already running!" exit 1 fi case "$1" in start) shift START_OPTION="console" HEART_OPTION="start" ;; start_boot) shift START_OPTION="console_boot" HEART_OPTION="start_boot" ;; esac RUN_PARAM=$(printf "\'%s\' " "$@") HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT $HEART_OPTION $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 $START_OPTION $RUN_PARAM" 2>&1 ;; stop) # Wait for the node to completely stop... case `uname -s` in Darwin) # Make sure we explicitly set this because iTerm.app doesn't for # some reason. COMMAND_MODE=unix2003 esac # Get the PID from nodetool get_pid GPR=$? if [ "$GPR" -ne 0 ] || [ -z $PID ]; then exit $GPR fi # Tell nodetool to initiate a stop $NODETOOL stop ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi # Wait for the node to completely stop... while `kill -s 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 ping_node ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi ;; attach) # Make sure a node is running ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift exec $ERTS_PATH/to_erl $PIPE_DIR ;; eval) # Make sure a node IS running ping_node > /dev/null 2>&1 ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi shift $NODETOOL eval "$1" ES_EVAL=$? if [ "$ES_EVAL" -ne 0 ]; then exit $ES_EVAL fi ;; remote_console) # Make sure a node is running ping_node 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 ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" exit $ES fi $NODETOOL upgrade $2 ;; console|console_clean|console_boot) # .boot file typically just $SCRIPT (ie, the app name) # however, for debugging, sometimes start_clean.boot is useful. # For e.g. 'setup', one may even want to name another boot script. case "$1" in console) BOOTFILE=$SCRIPT ;; console_clean) BOOTFILE=start_clean ;; console_boot) shift BOOTFILE="$1" shift ;; 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+"$@"} ;; getpid) # Get the PID from nodetool get_pid ES=$? if [ "$ES" -ne 0 ] || [ -z $PID ]; then exit $ES fi echo $PID ;; *) echo "Usage: $SCRIPT {start|start_boot |foreground|stop|restart|reboot|ping|console|getpid|console_clean|console_boot |attach|eval|remote_console|upgrade}" exit 1 ;; esac exit 0 rebar-2.6.0/test/upgrade_project/rel/files/erl000077500000000000000000000026211254103774400213520ustar00rootroot00000000000000#!/bin/sh # /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then POSIX_SHELL="true" export POSIX_SHELL exec /usr/bin/ksh $0 "$@" fi # clear it so if we invoke other scripts, they run as ksh as well unset POSIX_SHELL ## 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 -P) # 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.6.0/test/upgrade_project/rel/files/nodetool000066400000000000000000000157661254103774400224260ustar00rootroot00000000000000#!/usr/bin/env escript %% -*- 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), %% any commands that don't need a running node case RestArgs of ["chkconfig", File] -> case file:consult(File) of {ok, _} -> io:format("ok\n"), halt(0); {error, {Line, Mod, Term}} -> io:format(standard_error, ["Error on line ", file:format_error({Line, Mod, Term}), "\n"], []), halt(1); {error, R} -> io:format(standard_error, ["Error reading config file: ", file:format_error(R), "\n"], []), halt(1) end; _ -> ok end, %% 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; {false,pong} -> io:format("Failed to connect to node ~p .\n", [TargetNode]), halt(1); {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of ["getpid"] -> io:format("~p\n", [list_to_integer(rpc:call(TargetNode, os, getpid, []))]); ["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; ["rpc_infinity", Module, Function | RpcArgs] -> case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), [RpcArgs], infinity) 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; ["eval", Str0] -> Str = string:strip(Str0, right, $.) ++ ".", Bindings = erl_eval:new_bindings(), case rpc:call(TargetNode, erl_eval, exprs, [parse(Str), Bindings], 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), halt(1); {value, Value, _Bindings} -> io:format("~p\n", [Value]) end; Other -> io:format("Other: ~p\n", [Other]), io:format("Usage: nodetool {chkconfig|getpid|ping|stop|restart|reboot|rpc|rpc_infinity|rpcterms|eval}\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. parse(Str) -> {ok, Tokens, _} = erl_scan:string(Str), {ok, Exprs} = erl_parse:parse_exprs(Tokens), Exprs. rebar-2.6.0/test/upgrade_project/rel/files/sys.config000066400000000000000000000005151254103774400226470ustar00rootroot00000000000000[ %% 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.6.0/test/upgrade_project/rel/files/vm.args000066400000000000000000000006471254103774400221500ustar00rootroot00000000000000## 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.6.0/test/upgrade_project/rel/reltool.config000066400000000000000000000014331254103774400224070ustar00rootroot00000000000000{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"} ]}.