summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--manpages/commands25
-rw-r--r--manpages/rebar3.1423
-rw-r--r--src/rebar_erlc_compiler.erl16
-rw-r--r--src/rebar_file_utils.erl117
-rw-r--r--src/rebar_prv_edoc.erl36
-rw-r--r--src/rebar_prv_new.erl3
-rw-r--r--src/rebar_templater.erl20
-rw-r--r--test/rebar_compile_SUITE.erl144
-rw-r--r--test/rebar_edoc_SUITE.erl22
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.app.src16
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.erl9
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_app.erl26
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_sup.erl35
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.app.src16
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.erl12
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_app.erl26
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_sup.erl35
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.app.src17
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.erl19
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_app.erl26
-rw-r--r--test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_sup.erl35
-rw-r--r--test/rebar_file_utils_SUITE.erl190
23 files changed, 1224 insertions, 48 deletions
diff --git a/README.md b/README.md
index 6b95f91..ec33bea 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ best experience you can get.
A [getting started guide is maintained on the official documentation website](http://www.rebar3.org/docs/getting-started),
but installing rebar3 can be done by any of the ways described below
-Nightly compiled version:
+Latest stable compiled version:
```bash
$ wget https://s3.amazonaws.com/rebar3/rebar3 && chmod +x rebar3
```
@@ -69,7 +69,7 @@ $ cd rebar3
$ ./bootstrap
```
-Stable versions can be obtained from the [releases page](https://github.com/erlang/rebar3/releases).
+Stable versions can also be obtained from the [releases page](https://github.com/erlang/rebar3/releases).
The rebar3 escript can also extract itself with a run script under the user's home directory:
diff --git a/manpages/commands b/manpages/commands
new file mode 100644
index 0000000..b4db50a
--- /dev/null
+++ b/manpages/commands
@@ -0,0 +1,25 @@
+f(),
+P = application:get_env(rebar, providers, []),
+S = lists:foldl(fun(P, S) -> {ok, S2} = P:init(S), S2 end, rebar_state:new(), P),
+PS = rebar_state:providers(S),
+DP = lists:keysort(2,providers:get_providers_by_namespace(default, PS)),
+f(Str),
+Str = [begin
+ Name = element(2,Pn),
+ Desc = element(8,Pn),
+ Opts = element(10,Pn),
+ OptShort = [case {Short,Long} of
+ {undefined,undefined} -> "";
+ {undefined,_} -> ["[\\fI--",Long,"\\fR] "];
+ {_,undefined} -> ["[\\fI-",Short,"\\fR] "];
+ {_,_} -> ["[\\fI-",Short,"\\fR|\\fI--",Long,"\\fR] "]
+ end || {_,Short,Long,_,_Desc} <- Opts],
+ OptLong = [case {Short,Long} of
+ {undefined,undefined} -> "";
+ {_,undefined} -> [".IP\n\\fI-",Short,"\\fR: ", Desc, "\n"];
+ {_,_} -> [".IP\n\\fI--",Long,"\\fR: ", Desc, "\n"]
+ end || {_,Short,Long,_,Desc} <- Opts],
+ [".TP\n",
+ "\\fB", atom_to_list(element(2,Pn)), "\\fR ", OptShort, "\n",
+ Desc, "\n", OptLong] end || Pn <- DP, element(5,Pn) == true],
+file:write_file("commands.out", Str).
diff --git a/manpages/rebar3.1 b/manpages/rebar3.1
new file mode 100644
index 0000000..450e006
--- /dev/null
+++ b/manpages/rebar3.1
@@ -0,0 +1,423 @@
+.TH "REBAR3" "1" "January 2017" "Erlang"
+
+.SH NAME
+
+\fBrebar3\fR \- tool for working with Erlang projects
+
+.SH "SYNOPSIS"
+
+\fBrebar3\fR \fB\-\-version\fR
+
+.br
+\fBrebar3\fR \fBhelp\fR
+
+.br
+\fBrebar3\fR \fIcommand\fR [\fIoptions\fR] \.\.\.
+
+.SH "DESCRIPTION"
+
+Rebar3 is an Erlang tool that makes it easy to create, develop, and release Erlang libraries, applications, and systems in a repeatable manner\.
+
+Full documentation at \fIhttp://www.rebar3.org/\fR
+
+.SH "ESSENTIAL COMMANDS"
+
+For the full command list, see the \fICOMMANDS\fR section\.
+
+.P
+With \fBrebar3 help <task>\fR, commands and plugins will display their own help information\.
+
+.TP
+\fBcompile\fR
+Compile the current project
+
+.TP
+\fBnew\fR (\fBhelp [\fItemplate\fR])|\fItemplate\fR
+Show information about templates or use one
+
+.TP
+\fBupdate\fR
+Fetch the newest version of the Hex index
+
+.TP
+\fBdo\fR \fIcommand\fR[,\fIcommand\fR,...]
+Run one or more commands in a sequence
+
+.TP
+\fBshell\fR
+Start the current project in a REPL\. You can then use \fBr3:do(\fItask\fR)\fR to call it on the project without dropping the current state.
+
+.SH "COMMANDS"
+
+. this section generated by running the contents of 'commands' in rebar3 shell
+
+.TP
+\fBas\fR
+Higher order provider for running multiple tasks in a sequence as a certain profiles.
+.TP
+\fBclean\fR [\fI-a\fR|\fI--all\fR] [\fI-p\fR|\fI--profile\fR]
+Remove compiled beam files from apps.
+.IP
+\fI--all\fR: Clean all apps include deps
+.IP
+\fI--profile\fR: Clean under profile. Equivalent to `rebar3 as <profile> clean`
+.TP
+\fBcompile\fR
+Compile apps .app.src and .erl files.
+.TP
+\fBcover\fR [\fI-r\fR|\fI--reset\fR] [\fI-v\fR|\fI--verbose\fR]
+Perform coverage analysis.
+.IP
+\fI--reset\fR: Reset all coverdata.
+.IP
+\fI--verbose\fR: Print coverage analysis.
+.TP
+\fBct\fR [\fI--dir\fR] [\fI--suite\fR] [\fI--group\fR] [\fI--case\fR] [\fI--label\fR] [\fI--config\fR] [\fI--spec\fR] [\fI--join_specs\fR] [\fI--allow_user_terms\fR] [\fI--logdir\fR] [\fI--logopts\fR] [\fI--verbosity\fR] [\fI-c\fR|\fI--cover\fR] [\fI--repeat\fR] [\fI--duration\fR] [\fI--until\fR] [\fI--force_stop\fR] [\fI--basic_html\fR] [\fI--stylesheet\fR] [\fI--decrypt_key\fR] [\fI--decrypt_file\fR] [\fI--abort_if_missing_suites\fR] [\fI--multiply_timetraps\fR] [\fI--scale_timetraps\fR] [\fI--create_priv_dir\fR] [\fI--include\fR] [\fI--readable\fR] [\fI-v\fR|\fI--verbose\fR] [\fI--name\fR] [\fI--sname\fR] [\fI--setcookie\fR] [\fI--sys_config\fR]
+Run Common Tests.
+.IP
+\fI--dir\fR: List of additional directories containing test suites
+.IP
+\fI--suite\fR: List of test suites to run
+.IP
+\fI--group\fR: List of test groups to run
+.IP
+\fI--case\fR: List of test cases to run
+.IP
+\fI--label\fR: Test label
+.IP
+\fI--config\fR: List of config files
+.IP
+\fI--spec\fR: List of test specifications
+.IP
+\fI--join_specs\fR: Merge all test specifications and perform a single test run
+.IP
+\fI--allow_user_terms\fR: Allow user defined config values in config files
+.IP
+\fI--logdir\fR: Log folder
+.IP
+\fI--logopts\fR: Options for common test logging
+.IP
+\fI--verbosity\fR: Verbosity
+.IP
+\fI--cover\fR: Generate cover data
+.IP
+\fI--repeat\fR: How often to repeat tests
+.IP
+\fI--duration\fR: Max runtime (format: HHMMSS)
+.IP
+\fI--until\fR: Run until (format: HHMMSS)
+.IP
+\fI--force_stop\fR: Force stop on test timeout (true | false | skip_rest)
+.IP
+\fI--basic_html\fR: Show basic HTML
+.IP
+\fI--stylesheet\fR: CSS stylesheet to apply to html output
+.IP
+\fI--decrypt_key\fR: Path to key for decrypting config
+.IP
+\fI--decrypt_file\fR: Path to file containing key for decrypting config
+.IP
+\fI--abort_if_missing_suites\fR: Abort if suites are missing
+.IP
+\fI--multiply_timetraps\fR:
+.IP
+\fI--scale_timetraps\fR: Scale timetraps
+.IP
+\fI--create_priv_dir\fR: Create priv dir (auto_per_run | auto_per_tc | manual_per_tc)
+.IP
+\fI--include\fR: Directories containing additional include files
+.IP
+\fI--readable\fR: Shows test case names and only displays logs to shell on failures
+.IP
+\fI--verbose\fR: Verbose output
+.IP
+\fI--name\fR: Gives a long name to the node
+.IP
+\fI--sname\fR: Gives a short name to the node
+.IP
+\fI--setcookie\fR: Sets the cookie if the node is distributed
+.IP
+\fI--sys_config\fR: List of application config files
+.TP
+\fBdeps\fR
+List dependencies
+.TP
+\fBdialyzer\fR [\fI-u\fR|\fI--update-plt\fR] [\fI-s\fR|\fI--succ-typings\fR]
+Run the Dialyzer analyzer on the project.
+.IP
+\fI--update-plt\fR: Enable updating the PLT. Default: true
+.IP
+\fI--succ-typings\fR: Enable success typing analysis. Default: true
+.TP
+\fBdo\fR
+Higher order provider for running multiple tasks in a sequence.
+.TP
+\fBedoc\fR
+Generate documentation using edoc.
+.TP
+\fBescriptize\fR
+Generate escript archive.
+.TP
+\fBeunit\fR [\fI--app\fR] [\fI--application\fR] [\fI-c\fR|\fI--cover\fR] [\fI-d\fR|\fI--dir\fR] [\fI-f\fR|\fI--file\fR] [\fI-m\fR|\fI--module\fR] [\fI-s\fR|\fI--suite\fR] [\fI-v\fR|\fI--verbose\fR] [\fI--name\fR] [\fI--sname\fR] [\fI--setcookie\fR]
+Run EUnit Tests.
+.IP
+\fI--app\fR: Comma separated list of application test suites to run. Equivalent to `[{application, App}]`.
+.IP
+\fI--application\fR: Comma separated list of application test suites to run. Equivalent to `[{application, App}]`.
+.IP
+\fI--cover\fR: Generate cover data. Defaults to false.
+.IP
+\fI--dir\fR: Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`.
+.IP
+\fI--file\fR: Comma separated list of files to load tests from. Equivalent to `[{file, File}]`.
+.IP
+\fI--module\fR: Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.
+.IP
+\fI--suite\fR: Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.
+.IP
+\fI--verbose\fR: Verbose output. Defaults to false.
+.IP
+\fI--name\fR: Gives a long name to the node
+.IP
+\fI--sname\fR: Gives a short name to the node
+.IP
+\fI--setcookie\fR: Sets the cookie if the node is distributed
+.TP
+\fBget-deps\fR
+Fetch dependencies.
+.TP
+\fBhelp\fR
+Display a list of tasks or help for a given task or subtask.
+.TP
+\fBnew\fR [\fI-f\fR|\fI--force\fR]
+Create new project from templates.
+.IP
+\fI--force\fR: overwrite existing files
+.TP
+\fBpath\fR [\fI--app\fR] [\fI--base\fR] [\fI--bin\fR] [\fI--ebin\fR] [\fI--lib\fR] [\fI--priv\fR] [\fI-s\fR|\fI--separator\fR] [\fI--src\fR] [\fI--rel\fR]
+Print paths to build dirs in current profile.
+.IP
+\fI--app\fR: Comma seperated list of applications to return paths for.
+.IP
+\fI--base\fR: Return the `base' path of the current profile.
+.IP
+\fI--bin\fR: Return the `bin' path of the current profile.
+.IP
+\fI--ebin\fR: Return all `ebin' paths of the current profile's applications.
+.IP
+\fI--lib\fR: Return the `lib' path of the current profile.
+.IP
+\fI--priv\fR: Return the `priv' path of the current profile's applications.
+.IP
+\fI--separator\fR: In case of multiple return paths, the separator character to use to join them.
+.IP
+\fI--src\fR: Return the `src' path of the current profile's applications.
+.IP
+\fI--rel\fR: Return the `rel' path of the current profile.
+.TP
+\fBpkgs\fR
+List available packages.
+.TP
+\fBrelease\fR [\fI-n\fR|\fI--relname\fR] [\fI-v\fR|\fI--relvsn\fR] [\fI-g\fR|\fI--goal\fR] [\fI-u\fR|\fI--upfrom\fR] [\fI-o\fR|\fI--output-dir\fR] [\fI-h\fR|\fI--help\fR] [\fI-l\fR|\fI--lib-dir\fR] [\fI-p\fR|\fI--path\fR] [\fI--default-libs\fR] [\fI-V\fR|\fI--verbose\fR] [\fI-d\fR|\fI--dev-mode\fR] [\fI-i\fR|\fI--include-erts\fR] [\fI-a\fR|\fI--override\fR] [\fI-c\fR|\fI--config\fR] [\fI--overlay_vars\fR] [\fI--vm_args\fR] [\fI--sys_config\fR] [\fI--system_libs\fR] [\fI--version\fR] [\fI-r\fR|\fI--root\fR]
+Build release of project.
+.IP
+\fI--relname\fR: Specify the name for the release that will be generated
+.IP
+\fI--relvsn\fR: Specify the version for the release
+.IP
+\fI--goal\fR: Specify a target constraint on the system. These are usually the OTP
+.IP
+\fI--upfrom\fR: Only valid with relup target, specify the release to upgrade from
+.IP
+\fI--output-dir\fR: The output directory for the release. This is `./` by default.
+.IP
+\fI--help\fR: Print usage
+.IP
+\fI--lib-dir\fR: Additional dir that should be searched for OTP Apps
+.IP
+\fI--path\fR: Additional dir to add to the code path
+.IP
+\fI--default-libs\fR: Whether to use the default system added lib dirs (means you must add them all manually). Default is true
+.IP
+\fI--verbose\fR: Verbosity level, maybe between 0 and 3
+.IP
+\fI--dev-mode\fR: Symlink the applications and configuration into the release instead of copying
+.IP
+\fI--include-erts\fR: If true include a copy of erts used to build with, if a path include erts at that path. If false, do not include erts
+.IP
+\fI--override\fR: Provide an app name and a directory to override in the form <appname>:<app directory>
+.IP
+\fI--config\fR: The path to a config file
+.IP
+\fI--overlay_vars\fR: Path to a file of overlay variables
+.IP
+\fI--vm_args\fR: Path to a file to use for vm.args
+.IP
+\fI--sys_config\fR: Path to a file to use for sys.config
+.IP
+\fI--system_libs\fR: Path to dir of Erlang system libs
+.IP
+\fI--version\fR: Print relx version
+.IP
+\fI--root\fR: The project root directory
+.TP
+\fBrelup\fR [\fI-n\fR|\fI--relname\fR] [\fI-v\fR|\fI--relvsn\fR] [\fI-g\fR|\fI--goal\fR] [\fI-u\fR|\fI--upfrom\fR] [\fI-o\fR|\fI--output-dir\fR] [\fI-h\fR|\fI--help\fR] [\fI-l\fR|\fI--lib-dir\fR] [\fI-p\fR|\fI--path\fR] [\fI--default-libs\fR] [\fI-V\fR|\fI--verbose\fR] [\fI-d\fR|\fI--dev-mode\fR] [\fI-i\fR|\fI--include-erts\fR] [\fI-a\fR|\fI--override\fR] [\fI-c\fR|\fI--config\fR] [\fI--overlay_vars\fR] [\fI--vm_args\fR] [\fI--sys_config\fR] [\fI--system_libs\fR] [\fI--version\fR] [\fI-r\fR|\fI--root\fR]
+Create relup of releases.
+.IP
+\fI--relname\fR: Specify the name for the release that will be generated
+.IP
+\fI--relvsn\fR: Specify the version for the release
+.IP
+\fI--goal\fR: Specify a target constraint on the system. These are usually the OTP
+.IP
+\fI--upfrom\fR: Only valid with relup target, specify the release to upgrade from
+.IP
+\fI--output-dir\fR: The output directory for the release. This is `./` by default.
+.IP
+\fI--help\fR: Print usage
+.IP
+\fI--lib-dir\fR: Additional dir that should be searched for OTP Apps
+.IP
+\fI--path\fR: Additional dir to add to the code path
+.IP
+\fI--default-libs\fR: Whether to use the default system added lib dirs (means you must add them all manually). Default is true
+.IP
+\fI--verbose\fR: Verbosity level, maybe between 0 and 3
+.IP
+\fI--dev-mode\fR: Symlink the applications and configuration into the release instead of copying
+.IP
+\fI--include-erts\fR: If true include a copy of erts used to build with, if a path include erts at that path. If false, do not include erts
+.IP
+\fI--override\fR: Provide an app name and a directory to override in the form <appname>:<app directory>
+.IP
+\fI--config\fR: The path to a config file
+.IP
+\fI--overlay_vars\fR: Path to a file of overlay variables
+.IP
+\fI--vm_args\fR: Path to a file to use for vm.args
+.IP
+\fI--sys_config\fR: Path to a file to use for sys.config
+.IP
+\fI--system_libs\fR: Path to dir of Erlang system libs
+.IP
+\fI--version\fR: Print relx version
+.IP
+\fI--root\fR: The project root directory
+.TP
+\fBreport\fR
+Provide a crash report to be sent to the rebar3 issues page.
+.TP
+\fBshell\fR [\fI--config\fR] [\fI--name\fR] [\fI--sname\fR] [\fI--setcookie\fR] [\fI--script\fR] [\fI--apps\fR] [\fI--user_drv_args\fR]
+Run shell with project apps and deps in path.
+.IP
+\fI--config\fR: Path to the config file to use. Defaults to {shell, [{config, File}]} and then the relx sys.config file if not specified.
+.IP
+\fI--name\fR: Gives a long name to the node.
+.IP
+\fI--sname\fR: Gives a short name to the node.
+.IP
+\fI--setcookie\fR: Sets the cookie if the node is distributed.
+.IP
+\fI--script\fR: Path to an escript file to run before starting the project apps. Defaults to rebar.config {shell, [{script_file, File}]} if not specified.
+.IP
+\fI--apps\fR: A list of apps to boot before starting the shell. (E.g. --apps app1,app2,app3) Defaults to rebar.config {shell, [{apps, Apps}]} or relx apps if not specified.
+.IP
+\fI--user_drv_args\fR: Arguments passed to user_drv start function for creating custom shells.
+.TP
+\fBtar\fR [\fI-n\fR|\fI--relname\fR] [\fI-v\fR|\fI--relvsn\fR] [\fI-g\fR|\fI--goal\fR] [\fI-u\fR|\fI--upfrom\fR] [\fI-o\fR|\fI--output-dir\fR] [\fI-h\fR|\fI--help\fR] [\fI-l\fR|\fI--lib-dir\fR] [\fI-p\fR|\fI--path\fR] [\fI--default-libs\fR] [\fI-V\fR|\fI--verbose\fR] [\fI-d\fR|\fI--dev-mode\fR] [\fI-i\fR|\fI--include-erts\fR] [\fI-a\fR|\fI--override\fR] [\fI-c\fR|\fI--config\fR] [\fI--overlay_vars\fR] [\fI--vm_args\fR] [\fI--sys_config\fR] [\fI--system_libs\fR] [\fI--version\fR] [\fI-r\fR|\fI--root\fR]
+Tar archive of release built of project.
+.IP
+\fI--relname\fR: Specify the name for the release that will be generated
+.IP
+\fI--relvsn\fR: Specify the version for the release
+.IP
+\fI--goal\fR: Specify a target constraint on the system. These are usually the OTP
+.IP
+\fI--upfrom\fR: Only valid with relup target, specify the release to upgrade from
+.IP
+\fI--output-dir\fR: The output directory for the release. This is `./` by default.
+.IP
+\fI--help\fR: Print usage
+.IP
+\fI--lib-dir\fR: Additional dir that should be searched for OTP Apps
+.IP
+\fI--path\fR: Additional dir to add to the code path
+.IP
+\fI--default-libs\fR: Whether to use the default system added lib dirs (means you must add them all manually). Default is true
+.IP
+\fI--verbose\fR: Verbosity level, maybe between 0 and 3
+.IP
+\fI--dev-mode\fR: Symlink the applications and configuration into the release instead of copying
+.IP
+\fI--include-erts\fR: If true include a copy of erts used to build with, if a path include erts at that path. If false, do not include erts
+.IP
+\fI--override\fR: Provide an app name and a directory to override in the form <appname>:<app directory>
+.IP
+\fI--config\fR: The path to a config file
+.IP
+\fI--overlay_vars\fR: Path to a file of overlay variables
+.IP
+\fI--vm_args\fR: Path to a file to use for vm.args
+.IP
+\fI--sys_config\fR: Path to a file to use for sys.config
+.IP
+\fI--system_libs\fR: Path to dir of Erlang system libs
+.IP
+\fI--version\fR: Print relx version
+.IP
+\fI--root\fR: The project root directory
+.TP
+\fBtree\fR [\fI-v\fR|\fI--verbose\fR]
+Print dependency tree.
+.IP
+\fI--verbose\fR: Print repo and branch/tag/ref for git and hg deps
+.TP
+\fBunlock\fR
+Unlock dependencies.
+.TP
+\fBupdate\fR
+Update package index.
+.TP
+\fBupgrade\fR
+Upgrade dependencies.
+.TP
+\fBversion\fR
+Print version for rebar and current Erlang.
+.TP
+\fBxref\fR
+Run cross reference analysis.
+
+.SH ENVIRONMENT
+
+Environment variables allow overall rebar3 control across command boundaries.
+
+.TP
+\fBREBAR_PROFILE\fR
+Choose a default profile. Defaults to \fBdefault\fR
+
+.TP
+\fBHEX_CDN\fR
+Pick an alternative hex mirror.
+
+.TP
+\fBREBAR_CACHE_DIR\fR
+Location of the directory for local cache. Defaults to \fIhex.pm\fB.
+
+.TP
+\fBQUIET\fR
+Only display errors.
+
+.TP
+\fBDEBUG\fR
+Display debug information.
+
+.TP
+\fBREBAR_COLOR\fR=\fIhigh\fR|\fIlow\fR
+How much color to show in the terminal. Defaults to \fIhigh\fR.
+
+.TP
+\fBREBAR_CONFIG\fR
+Name of rebar configuration files. Defaults to \fIrebar.config\fR
+
+.SH Configuration File Options
+See \fIhttp://www.rebar3.org/v3.0/docs/configuration\fR
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index 95573fd..f7244dc 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -322,6 +322,10 @@ needed_files(G, ErlOpts, RebarOpts, Dir, OutDir, SourceFiles) ->
AllOpts = [{outdir, filename:dirname(Target)}
,{i, filename:join(Dir, "include")}
,{i, Dir}] ++ PrivIncludes ++ ErlOpts,
+ %% necessary for erlang:function_exported/3 to work as expected
+ %% called here for clarity as it's required by both opts_changed/2
+ %% and erl_compiler_opts_set/0
+ _ = code:ensure_loaded(compile),
digraph:vertex(G, Source) > {Source, filelib:last_modified(Target)}
orelse opts_changed(AllOpts, TargetBase)
orelse erl_compiler_opts_set()
@@ -342,8 +346,12 @@ maybe_rm_beam_and_edge(G, OutDir, Source) ->
end.
opts_changed(NewOpts, Target) ->
+ TotalOpts = case erlang:function_exported(compile, env_compiler_options, 0) of
+ true -> NewOpts ++ compile:env_compiler_options();
+ false -> NewOpts
+ end,
case compile_info(Target) of
- {ok, Opts} -> lists:sort(Opts) =/= lists:sort(NewOpts);
+ {ok, Opts} -> lists:sort(Opts) =/= lists:sort(TotalOpts);
_ -> true
end.
@@ -358,10 +366,12 @@ compile_info(Target) ->
end.
erl_compiler_opts_set() ->
- case os:getenv("ERL_COMPILER_OPTIONS") of
+ EnvSet = case os:getenv("ERL_COMPILER_OPTIONS") of
false -> false;
_ -> true
- end.
+ end,
+ %% return false if changed env opts would have been caught in opts_changed/2
+ EnvSet andalso not erlang:function_exported(compile, env_compiler_options, 0).
erlcinfo_file(Dir) ->
filename:join(rebar_dir:local_cache_dir(Dir), ?ERLCINFO_FILE).
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index 8645641..8158312 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -185,32 +185,113 @@ mv(Source, Dest) ->
ok
end;
{win32, _} ->
- Cmd = case filelib:is_dir(Source) of
- true ->
- ?FMT("robocopy /move /e \"~s\" \"~s\" 1> nul",
- [rebar_utils:escape_double_quotes(filename:nativename(Source)),
- rebar_utils:escape_double_quotes(filename:nativename(Dest))]);
- false ->
- ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\" 1> nul",
- [rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))),
- rebar_utils:escape_double_quotes(filename:nativename(Dest)),
- rebar_utils:escape_double_quotes(filename:basename(Source))])
- end,
- Res = rebar_utils:sh(Cmd,
- [{use_stdout, false}, return_on_error]),
- case win32_ok(Res) of
- true -> ok;
+ case filelib:is_dir(Source) of
+ true ->
+ SrcDir = filename:nativename(Source),
+ DestDir = case filelib:is_dir(Dest) of
+ true ->
+ %% to simulate unix/posix mv, we have to replicate
+ %% the same directory movement by moving the whole
+ %% top-level directory, not just the insides
+ SrcName = filename:basename(Source),
+ filename:nativename(filename:join(Dest, SrcName));
+ false ->
+ filename:nativename(Dest)
+ end,
+ robocopy_dir(SrcDir, DestDir);
false ->
- {error, lists:flatten(
- io_lib:format("Failed to move ~s to ~s~n",
- [Source, Dest]))}
+ SrcDir = filename:nativename(filename:dirname(Source)),
+ SrcName = filename:basename(Source),
+ DestDir = filename:nativename(filename:dirname(Dest)),
+ DestName = filename:basename(Dest),
+ IsDestDir = filelib:is_dir(Dest),
+ if IsDestDir ->
+ %% if basename and target name are different because
+ %% we move to a directory, then just move there.
+ %% Similarly, if they are the same but we're going to
+ %% a directory, let's just do that directly.
+ FullDestDir = filename:nativename(Dest),
+ robocopy_file(SrcDir, FullDestDir, SrcName)
+ ; SrcName =:= DestName ->
+ %% if basename and target name are the same and both are files,
+ %% we do a regular move with robocopy without rename.
+ robocopy_file(SrcDir, DestDir, DestName)
+ ; SrcName =/= DestName->
+ robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName)
+ end
+
end
end.
+robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) ->
+ %% If we're moving a file and the origin and
+ %% destination names are different:
+ %% - mktmp
+ %% - robocopy source_dir tmp_dir srcname
+ %% - rename srcname destname (to avoid clobbering)
+ %% - robocopy tmp_dir dest_dir destname
+ %% - remove tmp_dir
+ case ec_file:insecure_mkdtemp() of
+ {error, _Reason} ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s (tmpdir failed)~n",
+ [Source, Dest]))};
+ TmpPath ->
+ case robocopy_file(SrcDir, TmpPath, SrcName) of
+ {error, Reason} ->
+ {error, Reason};
+ ok ->
+ TmpSrc = filename:join(TmpPath, SrcName),
+ TmpDst = filename:join(TmpPath, DestName),
+ case file:rename(TmpSrc, TmpDst) of
+ {error, _} ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s (via rename)~n",
+ [Source, Dest]))};
+ ok ->
+ case robocopy_file(TmpPath, DestDir, DestName) of
+ Err = {error, _} -> Err;
+ OK -> rm_rf(TmpPath), OK
+ end
+ end
+ end
+ end.
+
+robocopy_file(SrcPath, DestPath, FileName) ->
+ Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\"",
+ [rebar_utils:escape_double_quotes(SrcPath),
+ rebar_utils:escape_double_quotes(DestPath),
+ rebar_utils:escape_double_quotes(FileName)]),
+ Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]),
+ case win32_ok(Res) of
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s~n",
+ [filename:join(SrcPath, FileName),
+ filename:join(DestPath, FileName)]))};
+ true ->
+ ok
+ end.
+
+robocopy_dir(Source, Dest) ->
+ Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\"",
+ [rebar_utils:escape_double_quotes(Source),
+ rebar_utils:escape_double_quotes(Dest)]),
+ Res = rebar_utils:sh(Cmd,
+ [{use_stdout, false}, return_on_error]),
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to move ~s to ~s~n",
+ [Source, Dest]))}
+ end.
+
win32_ok({ok, _}) -> true;
win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true;
win32_ok(_) -> false.
+
delete_each([]) ->
ok;
delete_each([File | Rest]) ->
diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl
index 465fc34..97f70a9 100644
--- a/src/rebar_prv_edoc.erl
+++ b/src/rebar_prv_edoc.erl
@@ -7,6 +7,7 @@
format_error/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-define(PROVIDER, edoc).
-define(DEPS, [compile]).
@@ -28,7 +29,8 @@ init(State) ->
{profiles, [docs]}])),
{ok, State1}.
--spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+-spec do(rebar_state:t()) ->
+ {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}.
do(State) ->
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
ProjectApps = rebar_state:project_apps(State),
@@ -37,26 +39,42 @@ do(State) ->
ShouldAccPaths = not has_configured_paths(EdocOpts),
Cwd = rebar_state:dir(State),
rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
- lists:foldl(fun(AppInfo, EdocOptsAcc) ->
+ Res = try
+ lists:foldl(fun(AppInfo, EdocOptsAcc) ->
rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State),
AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
?INFO("Running edoc for ~s", [AppName]),
AppDir = rebar_app_info:dir(AppInfo),
- ok = edoc:application(list_to_atom(AppName), AppDir, EdocOptsAcc),
+ AppRes = (catch edoc:application(list_to_atom(AppName), AppDir, EdocOptsAcc)),
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State),
- case ShouldAccPaths of
- true ->
+ case {AppRes, ShouldAccPaths} of
+ {ok, true} ->
%% edoc wants / on all OSes
add_to_paths(EdocOptsAcc, AppDir++"/doc");
- false ->
- EdocOptsAcc
+ {ok, false} ->
+ EdocOptsAcc;
+ {{'EXIT', error}, _} ->
+ %% EDoc is not very descriptive
+ %% in terms of failures
+ throw({app_failed, AppName})
end
- end, EdocOpts, ProjectApps),
+ end, EdocOpts, ProjectApps)
+ catch
+ {app_failed, AppName} ->
+ {app_failed, AppName}
+ end,
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
- {ok, State}.
+ case Res of
+ {app_failed, App} ->
+ ?PRV_ERROR({app_failed, App});
+ _ ->
+ {ok, State}
+ end.
-spec format_error(any()) -> iolist().
+format_error({app_failed, AppName}) ->
+ io_lib:format("Failed to generate documentation for app '~s'", [AppName]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl
index 064315e..152a56e 100644
--- a/src/rebar_prv_new.erl
+++ b/src/rebar_prv_new.erl
@@ -132,11 +132,14 @@ show_template({Name, Type, Location, Description, Vars}) ->
format_vars(Vars)]).
format_type(escript) -> "built-in";
+format_type(builtin) -> "built-in";
format_type(plugin) -> "plugin";
format_type(file) -> "custom".
format_type(escript, _) ->
"built-in template";
+format_type(builtin, _) ->
+ "built-in template";
format_type(plugin, Loc) ->
io_lib:format("plugin template (~s)", [Loc]);
format_type(file, Loc) ->
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index e64ce71..9b33ec5 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -267,8 +267,8 @@ find_templates(State) ->
PluginTemplates = find_plugin_templates(State),
{MainTemplates, Files} =
case rebar_state:escript_path(State) of
- undefined ->
- {find_priv_templates(State), []};
+ undefined -> % running in local install
+ {find_localinstall_templates(State), []};
_ ->
%% Cache the files since we'll potentially need to walk it several times
%% over the course of a run.
@@ -307,11 +307,10 @@ find_escript_templates(Files) ->
|| {Name, _Bin} <- Files,
re:run(Name, ?TEMPLATE_RE, [{capture, none}]) == match].
-find_priv_templates(State) ->
- OtherTemplates = rebar_utils:find_files(code:priv_dir(rebar), ?TEMPLATE_RE),
- HomeFiles = rebar_utils:find_files(rebar_dir:template_dir(State),
- ?TEMPLATE_RE, true), % recursive
- [{file, F} || F <- OtherTemplates ++ HomeFiles].
+find_localinstall_templates(_State) ->
+ Templates = rebar_utils:find_files(code:priv_dir(rebar), ?TEMPLATE_RE),
+ %% Pretend we're still running escripts; should work transparently.
+ [{builtin, F} || F <- Templates].
%% Fetch template indexes that sit on disk in the user's HOME
find_disk_templates(State) ->
@@ -354,6 +353,10 @@ prioritize_templates([{Name, Type, File} | Rest], Valid) ->
?DEBUG("Skipping template ~p, due to presence of a built-in "
"template with the same name", [Name]),
prioritize_templates(Rest, Valid);
+ {_, builtin, _} ->
+ ?DEBUG("Skipping template ~p, due to presence of a built-in "
+ "template with the same name", [Name]),
+ prioritize_templates(Rest, Valid);
{_, plugin, _} ->
?DEBUG("Skipping template ~p, due to presence of a plugin "
"template with the same name", [Name]),
@@ -369,6 +372,9 @@ prioritize_templates([{Name, Type, File} | Rest], Valid) ->
load_file(Files, escript, Name) ->
{Name, Bin} = lists:keyfind(Name, 1, Files),
Bin;
+load_file(_Files, builtin, Name) ->
+ {ok, Bin} = file:read_file(Name),
+ Bin;
load_file(_Files, plugin, Name) ->
{ok, Bin} = file:read_file(Name),
Bin;
diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl
index 3e4d5b9..9f01496 100644
--- a/test/rebar_compile_SUITE.erl
+++ b/test/rebar_compile_SUITE.erl
@@ -44,10 +44,13 @@
include_file_in_src/1,
include_file_relative_to_working_directory_test/1,
include_file_in_src_test/1,
+ dont_recompile_when_erl_compiler_options_env_does_not_change/1,
+ recompile_when_erl_compiler_options_env_changes/1,
always_recompile_when_erl_compiler_options_set/1,
recompile_when_parse_transform_inline_changes/1,
recompile_when_parse_transform_as_opt_changes/1,
- recursive/1,no_recursive/1]).
+ recursive/1,no_recursive/1,
+ regex_filter_skip/1, regex_filter_regression/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -74,10 +77,21 @@ all() ->
include_file_relative_to_working_directory_test, include_file_in_src_test,
recompile_when_parse_transform_as_opt_changes,
recompile_when_parse_transform_inline_changes,
- recursive, no_recursive] ++
- case erlang:function_exported(os, unsetenv, 1) of
- true -> [always_recompile_when_erl_compiler_options_set];
- false -> []
+ regex_filter_skip, regex_filter_regression,
+ %% recompile behaviour when `ERL_COMPILER_OPTIONS` differs prior to 19.x
+ recursive, no_recursive] ++ recompile_when_env_changes_test().
+
+recompile_when_env_changes_test() ->
+ _ = code:ensure_loaded(os),
+ UnSetEnv = erlang:function_exported(os, unsetenv, 1),
+ _ = code:ensure_loaded(compile),
+ EnvOpts = erlang:function_exported(compile, env_compiler_options, 0),
+ case {UnSetEnv, EnvOpts} of
+ {true, true} ->
+ [dont_recompile_when_erl_compiler_options_env_does_not_change,
+ recompile_when_erl_compiler_options_env_changes];
+ {true, false} -> [always_recompile_when_erl_compiler_options_set];
+ {false, _} -> []
end.
groups() ->
@@ -1385,7 +1399,51 @@ include_file_in_src_test(Config) ->
["as", "test", "compile"],
{ok, [{app, Name}]}).
-always_recompile_when_erl_compiler_options_set(Config) ->
+%% this test sets the env var, compiles, records the file last modified timestamp,
+%% recompiles and compares the file last modified timestamp to ensure it hasn't
+%% changed. this test should run on 19.x+
+dont_recompile_when_erl_compiler_options_env_does_not_change(Config) ->
+ %% save existing env to restore after test
+ ExistingEnv = os:getenv("ERL_COMPILER_OPTIONS"),
+
+ AppDir = ?config(apps, Config),
+
+ Name = rebar_test_utils:create_random_name("erl_compiler_options_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ true = os:unsetenv("ERL_COMPILER_OPTIONS"),
+
+ true = os:putenv("ERL_COMPILER_OPTIONS", "[{d, some_macro}]"),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
+
+ {ok, Files} = rebar_utils:list_dir(EbinDir),
+ ModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- Files, filename:extension(F) == ".beam"],
+
+ timer:sleep(1000),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ {ok, NewFiles} = rebar_utils:list_dir(EbinDir),
+ NewModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- NewFiles, filename:extension(F) == ".beam"],
+
+ ?assert(ModTime == NewModTime),
+
+ %% restore existing env
+ case ExistingEnv of
+ false -> ok;
+ _ -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv)
+ end.
+
+%% this test compiles, records the file last modified timestamp, sets the env
+%% var, recompiles and compares the file last modified timestamp to ensure it
+%% has changed. this test should run on 19.x+
+recompile_when_erl_compiler_options_env_changes(Config) ->
%% save existing env to restore after test
ExistingEnv = os:getenv("ERL_COMPILER_OPTIONS"),
@@ -1423,6 +1481,47 @@ always_recompile_when_erl_compiler_options_set(Config) ->
_ -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv)
end.
+%% this test sets the env var, compiles, records the file last modified
+%% timestamp, recompiles and compares the file last modified timestamp to
+%% ensure it has changed. this test should run on 18.x
+always_recompile_when_erl_compiler_options_set(Config) ->
+ %% save existing env to restore after test
+ ExistingEnv = os:getenv("ERL_COMPILER_OPTIONS"),
+
+ AppDir = ?config(apps, Config),
+
+ Name = rebar_test_utils:create_random_name("erl_compiler_options_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ true = os:unsetenv("ERL_COMPILER_OPTIONS"),
+
+ true = os:putenv("ERL_COMPILER_OPTIONS", "[{d, some_macro}]"),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
+
+ {ok, Files} = rebar_utils:list_dir(EbinDir),
+ ModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- Files, filename:extension(F) == ".beam"],
+
+ timer:sleep(1000),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ {ok, NewFiles} = rebar_utils:list_dir(EbinDir),
+ NewModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- NewFiles, filename:extension(F) == ".beam"],
+
+ ?assert(ModTime =/= NewModTime),
+
+ %% restore existing env
+ case ExistingEnv of
+ false -> ok;
+ _ -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv)
+ end.
+
recompile_when_parse_transform_inline_changes(Config) ->
AppDir = ?config(apps, Config),
@@ -1556,5 +1655,38 @@ no_recursive(Config) ->
{ok, [{app, Name}]}),
{ok, Files2} = rebar_utils:list_dir(EbinDir),
?assert(false==lists:member("rec.beam",Files2)),
+ ok.
+
+regex_filter_skip(Config) ->
+ AppDir = ?config(apps, Config),
+ Name = rebar_test_utils:create_random_name("regex_skip"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+ rebar_test_utils:write_src_file(filename:join(AppDir,src),"._rec.erl"),
+ Expected = filename:join([AppDir, "_build", "default", "lib", Name, "ebin","._rec.beam"]),
+
+ RebarConfig = [],
+ try
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"],
+ {ok, [{file, Expected}]}),
+ throw(should_not_be_found)
+ catch
+ %% the file was not found, as desired!
+ error:{assertion_failed,_} -> %% OTP =< 17
+ ok;
+ error:{assert,_} -> %% OTP >= 18
+ ok
+ end.
+regex_filter_regression(Config) ->
+ AppDir = ?config(apps, Config),
+ Name = rebar_test_utils:create_random_name("regex_regression"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+ rebar_test_utils:write_src_file(filename:join(AppDir,src),"r_f.erl"),
+ Expected = filename:join([AppDir, "_build", "default", "lib", Name, "ebin","r_f.beam"]),
+ RebarConfig = [],
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"],
+ {ok, [{file, Expected}]}),
ok.
+
diff --git a/test/rebar_edoc_SUITE.erl b/test/rebar_edoc_SUITE.erl
index fded2b0..2c4aba5 100644
--- a/test/rebar_edoc_SUITE.erl
+++ b/test/rebar_edoc_SUITE.erl
@@ -3,7 +3,7 @@
-include_lib("eunit/include/eunit.hrl").
-compile(export_all).
-all() -> [multiapp].
+all() -> [multiapp, error_survival].
init_per_testcase(multiapp, Config) ->
application:load(rebar),
@@ -16,6 +16,18 @@ init_per_testcase(multiapp, Config) ->
rebar_log:init(command_line, Verbosity),
State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
,{root_dir, AppsDir}]),
+ [{apps, AppsDir}, {state, State}, {name, Name} | Config];
+init_per_testcase(error_survival, Config) ->
+ application:load(rebar),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Name = rebar_test_utils:create_random_name("error_survival"),
+ AppsDir = filename:join([PrivDir, rebar_test_utils:create_random_name(Name)]),
+ ec_file:copy(filename:join([DataDir, "bad"]), AppsDir, [recursive]),
+ Verbosity = rebar3:log_level(),
+ rebar_log:init(command_line, Verbosity),
+ State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
+ ,{root_dir, AppsDir}]),
[{apps, AppsDir}, {state, State}, {name, Name} | Config].
end_per_testcase(_, Config) ->
@@ -42,6 +54,14 @@ multiapp(Config) ->
filename:join([AppsDir, "apps", "foo", "doc", "foo.html"]),
"apps/bar1/doc/bar1.html")).
+error_survival(Config) ->
+ RebarConfig = [],
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["edoc"],
+ {error,{rebar_prv_edoc,{app_failed,"bar2"}}}
+ ),
+ ok.
+
file_content_matches(Path, Regex) ->
case file:read_file(Path) of
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.app.src b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.app.src
new file mode 100644
index 0000000..6e7ec24
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.app.src
@@ -0,0 +1,16 @@
+{application, bar1,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { bar1_app, []}},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.erl b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.erl
new file mode 100644
index 0000000..2700aef
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1.erl
@@ -0,0 +1,9 @@
+-module(bar1).
+-export([bar1/0]).
+-export_type([barer1/0]).
+
+-type barer1() :: string().
+
+% @doc Bar1 bars the bar.
+-spec bar1() -> barer1().
+bar1() -> "Barer1". \ No newline at end of file
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_app.erl b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_app.erl
new file mode 100644
index 0000000..414ac30
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_app.erl
@@ -0,0 +1,26 @@
+%%%-------------------------------------------------------------------
+%% @doc bar1 public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar1_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ bar1_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_sup.erl b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_sup.erl
new file mode 100644
index 0000000..f9d6670
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar1/src/bar1_sup.erl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc bar1 top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar1_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ {ok, { {one_for_all, 0, 1}, []} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.app.src b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.app.src
new file mode 100644
index 0000000..58de8bc
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.app.src
@@ -0,0 +1,16 @@
+{application, bar2,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { bar2_app, []}},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.erl b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.erl
new file mode 100644
index 0000000..2afb745
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2.erl
@@ -0,0 +1,12 @@
+%% @doc one docline is fine
+%% @doc a second docline causes a failure
+%% @doc if not, then a & causes a bad ref error.
+-module(bar2).
+-export([bar2/0]).
+-export_type([barer2/0]).
+
+-type barer2() :: string().
+
+% @doc Bar2 bars the bar2.
+-spec bar2() -> barer2().
+bar2() -> "Barer2".
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_app.erl b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_app.erl
new file mode 100644
index 0000000..d0058a0
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_app.erl
@@ -0,0 +1,26 @@
+%%%-------------------------------------------------------------------
+%% @doc bar2 public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar2_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ bar2_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_sup.erl b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_sup.erl
new file mode 100644
index 0000000..0bdaf4a
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/bar2/src/bar2_sup.erl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc bar2 top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar2_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ {ok, { {one_for_all, 0, 1}, []} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.app.src b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.app.src
new file mode 100644
index 0000000..9987fd5
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.app.src
@@ -0,0 +1,17 @@
+{application, foo,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { foo_app, []}},
+ {applications,
+ [kernel,
+ stdlib,
+ bar1, bar2
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.erl b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.erl
new file mode 100644
index 0000000..52e3d0a
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo.erl
@@ -0,0 +1,19 @@
+-module(foo).
+
+-export([foo/0, bar1/0, bar2/0]).
+
+-export_type([fooer/0]).
+
+-type fooer() :: string().
+
+% @doc Foo function returns fooer.
+-spec foo() -> fooer().
+foo() -> "fooer".
+
+% @doc Bar1 function returns barer1.
+-spec bar1() -> bar1:barer1().
+bar1() -> bar1:bar1().
+
+% @doc Bar2 functions returns barer2.
+-spec bar2() -> bar2:barer2().
+bar2() -> bar2:bar2(). \ No newline at end of file
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_app.erl b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_app.erl
new file mode 100644
index 0000000..d0158d7
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_app.erl
@@ -0,0 +1,26 @@
+%%%-------------------------------------------------------------------
+%% @doc foo public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(foo_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ foo_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_sup.erl b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_sup.erl
new file mode 100644
index 0000000..67e88b4
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/bad/apps/foo/src/foo_sup.erl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc foo top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(foo_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ {ok, { {one_for_all, 0, 1}, []} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl
index 7285e13..4cc6a93 100644
--- a/test/rebar_file_utils_SUITE.erl
+++ b/test/rebar_file_utils_SUITE.erl
@@ -4,6 +4,8 @@
groups/0,
init_per_group/2,
end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
raw_tmpdir/1,
empty_tmpdir/1,
simple_tmpdir/1,
@@ -15,7 +17,13 @@
canonical_path/1,
resolve_link/1,
split_dirname/1,
- mv_warning_is_ignored/1]).
+ mv_warning_is_ignored/1,
+ mv_dir/1,
+ mv_file_same/1,
+ mv_file_diff/1,
+ mv_file_dir_same/1,
+ mv_file_dir_diff/1,
+ mv_no_clobber/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -25,6 +33,7 @@
all() ->
[{group, tmpdir},
{group, reset_dir},
+ {group, mv},
path_from_ancestor,
canonical_path,
resolve_link,
@@ -33,7 +42,9 @@ all() ->
groups() ->
[{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]},
- {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]}].
+ {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]},
+ {mv, [], [mv_dir, mv_file_same, mv_file_diff,
+ mv_file_dir_same, mv_file_dir_diff, mv_no_clobber]}].
init_per_group(reset_dir, Config) ->
TmpDir = rebar_file_utils:system_tmpdir(["rebar_file_utils_SUITE", "resetable"]),
@@ -41,6 +52,20 @@ init_per_group(reset_dir, Config) ->
init_per_group(_, Config) -> Config.
end_per_group(_, Config) -> Config.
+init_per_testcase(Test, Config) ->
+ case os:type() of
+ {win32, _} ->
+ case lists:member(Test, [resolve_link, mv_warning_is_ignored]) of
+ true -> {skip, "broken in windows"};
+ false -> Config
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_testcase(_Test, Config) ->
+ Config.
+
raw_tmpdir(_Config) ->
case rebar_file_utils:system_tmpdir() of
"/tmp" -> ok;
@@ -143,3 +168,164 @@ mv_warning_is_ignored(_Config) ->
meck:expect(rebar_utils, sh, fun("mv ding dong", _) -> {ok, "Warning"} end),
ok = rebar_file_utils:mv("ding", "dong"),
meck:unload(rebar_utils).
+
+%%% Ensure Windows & Unix operations to move files
+
+mv_dir(Config) ->
+ %% Move a directory to another one location
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_dir),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ %% empty dir movement
+ DstDir1 = filename:join(BaseDir, "dst1/"),
+ ?assertNot(filelib:is_dir(DstDir1)),
+ ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir1)),
+ ?assert(filelib:is_dir(DstDir1)),
+
+ %% move files from dir to empty dir
+ F1 = filename:join(SrcDir, "file1"),
+ F2 = filename:join(SrcDir, "subdir/file2"),
+ filelib:ensure_dir(F2),
+ file:write_file(F1, "hello"),
+ file:write_file(F2, "world"),
+ DstDir2 = filename:join(BaseDir, "dst2/"),
+ D2F1 = filename:join(DstDir2, "file1"),
+ D2F2 = filename:join(DstDir2, "subdir/file2"),
+ ?assertNot(filelib:is_dir(DstDir2)),
+ ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir2)),
+ ?assert(filelib:is_file(D2F1)),
+ ?assert(filelib:is_file(D2F2)),
+
+ %% move files from dir to existing dir moves it to
+ %% a subdir
+ filelib:ensure_dir(F2),
+ file:write_file(F1, "hello"),
+ file:write_file(F2, "world"),
+ DstDir3 = filename:join(BaseDir, "dst3/"),
+ D3F1 = filename:join(DstDir3, "src/file1"),
+ D3F2 = filename:join(DstDir3, "src/subdir/file2"),
+ ec_file:mkdir_p(DstDir3),
+ ?assert(filelib:is_dir(DstDir3)),
+ ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir3)),
+ ?assertNot(filelib:is_file(F1)),
+ ?assertNot(filelib:is_file(F2)),
+ ?assert(filelib:is_file(D3F1)),
+ ?assert(filelib:is_file(D3F2)),
+ ?assertNot(filelib:is_dir(SrcDir)),
+ ok.
+
+mv_file_same(Config) ->
+ %% Move a file from a directory to the other without renaming
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_same),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file"),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_file_diff(Config) ->
+ %% Move a file from a directory to another one while renaming
+ %% into a pre-existing file
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_diff),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file-rename"),
+ file:write_file(Dst, "not-the-right-content"),
+ ?assert(filelib:is_file(Dst)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_file_dir_same(Config) ->
+ %% Move a file to a directory without renaming
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_dir_same),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file"),
+ ?assert(filelib:is_dir(DstDir)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, DstDir)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_file_dir_diff(Config) ->
+ %% Move a file to a directory while renaming
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_file_dir_diff),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file-rename"),
+ ?assert(filelib:is_dir(DstDir)),
+ ?assertNot(filelib:is_file(Dst)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ok.
+
+mv_no_clobber(Config) ->
+ %% Moving a file while renaming does not clobber other files
+ PrivDir = ?config(priv_dir, Config),
+ BaseDir = mk_base_dir(PrivDir, mv_no_clobber),
+ SrcDir = filename:join(BaseDir, "src/"),
+ ec_file:mkdir_p(SrcDir),
+ ?assert(filelib:is_dir(SrcDir)),
+ F = filename:join(SrcDir, "file"),
+ file:write_file(F, "hello"),
+ FBad = filename:join(SrcDir, "file-alt"),
+ file:write_file(FBad, "wrong-data"),
+ DstDir = filename:join(BaseDir, "dst/"),
+ ec_file:mkdir_p(DstDir),
+ Dst = filename:join(DstDir, "file-alt"),
+ DstBad = filename:join(DstDir, "file"),
+ file:write_file(DstBad, "wrong-data"),
+ ?assert(filelib:is_file(F)),
+ ?assert(filelib:is_file(FBad)),
+ ?assert(filelib:is_dir(DstDir)),
+ ?assertNot(filelib:is_file(Dst)),
+ ?assert(filelib:is_file(DstBad)),
+ ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
+ ?assert(filelib:is_file(Dst)),
+ ?assertNot(filelib:is_file(F)),
+ ?assert(filelib:is_file(DstBad)),
+ ?assert(filelib:is_file(FBad)),
+ ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)),
+ ?assertEqual({ok, <<"wrong-data">>}, file:read_file(FBad)),
+ ?assertEqual({ok, <<"wrong-data">>}, file:read_file(DstBad)),
+ ok.
+
+
+mk_base_dir(BasePath, Name) ->
+ {_,_,Micro} = os:timestamp(),
+ Index = integer_to_list(Micro),
+ Path = filename:join(BasePath, atom_to_list(Name) ++ Index),
+ ec_file:mkdir_p(Path),
+ Path.