diff options
23 files changed, 1224 insertions, 48 deletions
@@ -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. |