diff options
-rw-r--r-- | README.md | 3 | ||||
-rwxr-xr-x | bootstrap | 159 | ||||
-rw-r--r-- | priv/templates/LICENSE (renamed from priv/templates/LICENSE.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/Makefile (renamed from priv/templates/Makefile.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/README.md (renamed from priv/templates/README.md.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/app.erl (renamed from priv/templates/app.erl.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/app.template | 14 | ||||
-rw-r--r-- | priv/templates/cmake.template | 2 | ||||
-rw-r--r-- | priv/templates/escript.template | 11 | ||||
-rw-r--r-- | priv/templates/escript_README.md | 14 | ||||
-rw-r--r-- | priv/templates/escript_mod.erl | 17 | ||||
-rw-r--r-- | priv/templates/escript_rebar.config | 13 | ||||
-rw-r--r-- | priv/templates/gitignore (renamed from priv/templates/gitignore.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/lib.template | 12 | ||||
-rw-r--r-- | priv/templates/mod.erl (renamed from priv/templates/mod.erl.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/otp_app.app.src (renamed from priv/templates/otp_app.app.src.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/otp_lib.app.src (renamed from priv/templates/otp_lib.app.src.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/plugin.erl (renamed from priv/templates/plugin.erl.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/plugin.template | 12 | ||||
-rw-r--r-- | priv/templates/plugin_README.md (renamed from priv/templates/plugin_README.md.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/rebar.config (renamed from priv/templates/rebar.config.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/release.template | 18 | ||||
-rw-r--r-- | priv/templates/relx_rebar.config (renamed from priv/templates/relx_rebar.config.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/sup.erl (renamed from priv/templates/sup.erl.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/sys.config (renamed from priv/templates/sys.config.dtl) | 0 | ||||
-rw-r--r-- | priv/templates/vm.args (renamed from priv/templates/vm.args.dtl) | 0 | ||||
-rw-r--r-- | rebar.config | 61 | ||||
-rw-r--r-- | rebar.lock | 24 | ||||
-rw-r--r-- | src/rebar.app.src | 3 | ||||
-rw-r--r-- | src/rebar3.erl | 12 | ||||
-rw-r--r-- | src/rebar_api.erl | 6 | ||||
-rw-r--r-- | src/rebar_app_discover.erl | 13 | ||||
-rw-r--r-- | src/rebar_dir.erl | 2 | ||||
-rw-r--r-- | src/rebar_erlc_compiler.erl | 33 | ||||
-rw-r--r-- | src/rebar_git_resource.erl | 41 | ||||
-rw-r--r-- | src/rebar_otp_app.erl | 7 | ||||
-rw-r--r-- | src/rebar_packages.erl | 24 | ||||
-rw-r--r-- | src/rebar_plugins.erl | 4 | ||||
-rw-r--r-- | src/rebar_prv_app_discovery.erl | 8 | ||||
-rw-r--r-- | src/rebar_prv_common_test.erl | 42 | ||||
-rw-r--r-- | src/rebar_prv_deps.erl | 119 | ||||
-rw-r--r-- | src/rebar_prv_erlydtl_compiler.erl | 270 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 50 | ||||
-rw-r--r-- | src/rebar_prv_shell.erl | 62 | ||||
-rw-r--r-- | src/rebar_prv_update.erl | 28 | ||||
-rw-r--r-- | src/rebar_prv_upgrade.erl | 1 | ||||
-rw-r--r-- | src/rebar_state.erl | 56 | ||||
-rw-r--r-- | src/rebar_templater.erl | 77 | ||||
-rw-r--r-- | src/rebar_utils.erl | 104 | ||||
-rw-r--r-- | test/mock_pkg_resource.erl | 2 | ||||
-rw-r--r-- | test/rebar_compile_SUITE.erl | 27 | ||||
-rw-r--r-- | test/rebar_ct_SUITE.erl | 36 | ||||
-rw-r--r-- | test/rebar_erlydtl_SUITE.erl | 72 | ||||
-rw-r--r-- | test/rebar_profiles_SUITE.erl | 150 | ||||
-rw-r--r-- | test/rebar_test_utils.erl | 4 |
55 files changed, 899 insertions, 714 deletions
@@ -13,7 +13,7 @@ configuration work. rebar also provides dependency management, enabling application writers to easily re-use common libraries from a variety of locations (git, hg, etc). -3.0 Alpha-4 +3.0 Alpha-5 ==== [DOCUMENTATION](http://www.rebar3.org/v3.0/docs) @@ -44,6 +44,7 @@ limit scope. | new | Create new rebar project from templates | | pkgs | List available packages | | release | Build release of project | +| report | Report on environment and versions for bug reports | | shell | Run shell with project apps in path | | tar | Package release into tarball | | update | Update package index | @@ -2,6 +2,7 @@ %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et + main(_Args) -> %% Fetch and build deps required to build rebar3 BaseDeps = [{providers, []} @@ -26,7 +27,7 @@ main(_Args) -> {ok, State} = rebar3:run(["compile"]), reset_env(), os:putenv("REBAR_PROFILE", ""), - %% Build erlydtl files (a hook on compile in the default profile) and escript file + DepsPaths = rebar_state:code_paths(State, all_deps), code:add_pathsa(DepsPaths), @@ -105,23 +106,124 @@ compile_file(File, Opts) -> bootstrap_rebar3() -> filelib:ensure_dir("_build/default/lib/rebar/ebin/dummy.beam"), code:add_path("_build/default/lib/rebar/ebin/"), - file:make_symlink(filename:absname("src"), filename:absname("_build/default/lib/rebar/src")), + ok = symlink_or_copy(filename:absname("src"), + filename:absname("_build/default/lib/rebar/src")), Sources = ["src/rebar_resource.erl" | filelib:wildcard("src/*.erl")], [compile_file(X, [{outdir, "_build/default/lib/rebar/ebin/"} ,return | additional_defines()]) || X <- Sources], code:add_patha(filename:absname("_build/default/lib/rebar/ebin")). +%%rebar.hrl +-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))). +%%/rebar.hrl +%%rebar_file_utils +symlink_or_copy(Source, Target) -> + Link = case os:type() of + {win32, _} -> + Source; + _ -> + make_relative_path(Source, Target) + end, + case file:make_symlink(Link, Target) of + ok -> + ok; + {error, eexist} -> + ok; + {error, _} -> + cp_r([Source], Target) + end. + +make_relative_path(Source, Target) -> + do_make_relative_path(filename:split(Source), filename:split(Target)). + +do_make_relative_path([H|T1], [H|T2]) -> + do_make_relative_path(T1, T2); +do_make_relative_path(Source, Target) -> + Base = lists:duplicate(max(length(Target) - 1, 0), ".."), + filename:join(Base ++ Source). + +cp_r([], _Dest) -> + ok; +cp_r(Sources, Dest) -> + case os:type() of + {unix, _} -> + EscSources = [escape_path(Src) || Src <- Sources], + SourceStr = string:join(EscSources, " "), + os:cmd(?FMT("cp -R ~s \"~s\"", [SourceStr, Dest])), + ok; + {win32, _} -> + lists:foreach(fun(Src) -> ok = cp_r_win32(Src,Dest) end, Sources), + ok + end. + +xcopy_win32(Source,Dest)-> + R = os:cmd(?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul", + [filename:nativename(Source), filename:nativename(Dest)])), + case length(R) > 0 of + %% when xcopy fails, stdout is empty and and error message is printed + %% to stderr (which is redirected to nul) + true -> ok; + false -> + {error, lists:flatten( + io_lib:format("Failed to xcopy from ~s to ~s~n", + [Source, Dest]))} + end. + +cp_r_win32({true, SourceDir}, {true, DestDir}) -> + %% from directory to directory + SourceBase = filename:basename(SourceDir), + ok = case file:make_dir(filename:join(DestDir, SourceBase)) of + {error, eexist} -> ok; + Other -> Other + end, + ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase)); +cp_r_win32({false, Source} = S,{true, DestDir}) -> + %% from file to directory + cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))}); +cp_r_win32({false, Source},{false, Dest}) -> + %% from file to file + {ok,_} = file:copy(Source, Dest), + ok; +cp_r_win32({true, SourceDir}, {false, DestDir}) -> + case filelib:is_regular(DestDir) of + true -> + %% From directory to file? This shouldn't happen + {error, lists:flatten( + io_lib:format("Cannot copy dir (~p) to file (~p)\n", + [SourceDir, DestDir]))}; + false -> + %% Specifying a target directory that doesn't currently exist. + %% So let's attempt to create this directory + case filelib:ensure_dir(filename:join(DestDir, "dummy")) of + ok -> + ok = xcopy_win32(SourceDir, DestDir); + {error, Reason} -> + {error, lists:flatten( + io_lib:format("Unable to create dir ~p: ~p\n", + [DestDir, Reason]))} + end + end; +cp_r_win32(Source,Dest) -> + Dst = {filelib:is_dir(Dest), Dest}, + lists:foreach(fun(Src) -> + ok = cp_r_win32({filelib:is_dir(Src), Src}, Dst) + end, filelib:wildcard(Source)), + ok. + +escape_path(Str) -> + re:replace(Str, "([ ()?])", "\\\\&", [global, {return, list}]). +%%/rebar_file_utils + setup_env() -> - %% We don't need or want erlydtl or relx providers loaded yet + %% We don't need or want relx providers loaded yet application:load(rebar), {ok, Providers} = application:get_env(rebar, providers), - Providers1 = Providers -- [rebar_prv_erlydtl_compiler, - rebar_prv_release, + Providers1 = Providers -- [rebar_prv_release, rebar_prv_tar], application:set_env(rebar, providers, Providers1). reset_env() -> - %% Reset the env so we get all providers and can build erlydtl files + %% Reset the env so we get all providers application:unset_env(rebar, providers), application:unload(rebar), application:load(rebar). @@ -196,25 +298,28 @@ otp_release1([$R,N|_]=Rel) when is_integer(N) -> %% the "\n". otp_release1(Rel) -> File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]), - {ok, Vsn} = file:read_file(File), - - %% It's fine to rely on the binary module here because we can - %% be sure that it's available when the otp_release string does - %% not begin with $R. - Size = byte_size(Vsn), - %% The shortest vsn string consists of at least two digits - %% followed by "\n". Therefore, it's safe to assume Size >= 3. - case binary:part(Vsn, {Size, -3}) of - <<"**\n">> -> - %% The OTP documentation mentions that a system patched - %% using the otp_patch_apply tool available to licensed - %% customers will leave a '**' suffix in the version as a - %% flag saying the system consists of application versions - %% from multiple OTP versions. We ignore this flag and - %% drop the suffix, given for all intents and purposes, we - %% cannot obtain relevant information from it as far as - %% tooling is concerned. - binary:bin_to_list(Vsn, {0, Size - 3}); - _ -> - binary:bin_to_list(Vsn, {0, Size - 1}) + case file:read_file(File) of + {error, _} -> + Rel; + {ok, Vsn} -> + %% It's fine to rely on the binary module here because we can + %% be sure that it's available when the otp_release string does + %% not begin with $R. + Size = byte_size(Vsn), + %% The shortest vsn string consists of at least two digits + %% followed by "\n". Therefore, it's safe to assume Size >= 3. + case binary:part(Vsn, {Size, -3}) of + <<"**\n">> -> + %% The OTP documentation mentions that a system patched + %% using the otp_patch_apply tool available to licensed + %% customers will leave a '**' suffix in the version as a + %% flag saying the system consists of application versions + %% from multiple OTP versions. We ignore this flag and + %% drop the suffix, given for all intents and purposes, we + %% cannot obtain relevant information from it as far as + %% tooling is concerned. + binary:bin_to_list(Vsn, {0, Size - 3}); + _ -> + binary:bin_to_list(Vsn, {0, Size - 1}) + end end. diff --git a/priv/templates/LICENSE.dtl b/priv/templates/LICENSE index 41588ab..41588ab 100644 --- a/priv/templates/LICENSE.dtl +++ b/priv/templates/LICENSE diff --git a/priv/templates/Makefile.dtl b/priv/templates/Makefile index d3c3767..d3c3767 100644 --- a/priv/templates/Makefile.dtl +++ b/priv/templates/Makefile diff --git a/priv/templates/README.md.dtl b/priv/templates/README.md index 5507536..5507536 100644 --- a/priv/templates/README.md.dtl +++ b/priv/templates/README.md diff --git a/priv/templates/app.erl.dtl b/priv/templates/app.erl index 83eb9a3..83eb9a3 100644 --- a/priv/templates/app.erl.dtl +++ b/priv/templates/app.erl diff --git a/priv/templates/app.template b/priv/templates/app.template index b17e30c..4087e5e 100644 --- a/priv/templates/app.template +++ b/priv/templates/app.template @@ -3,10 +3,10 @@ {name, "mylib", "Name of the OTP application"}, {desc, "An OTP application", "Short description of the app"} ]}. -{template, "app.erl.dtl", "{{name}}/src/{{name}}_app.erl"}. -{template, "sup.erl.dtl", "{{name}}/src/{{name}}_sup.erl"}. -{template, "otp_app.app.src.dtl", "{{name}}/src/{{name}}.app.src"}. -{template, "rebar.config.dtl", "{{name}}/rebar.config"}. -{template, "gitignore.dtl", "{{name}}/.gitignore"}. -{template, "LICENSE.dtl", "{{name}}/LICENSE"}. -{template, "README.md.dtl", "{{name}}/README.md"}. +{template, "app.erl", "{{name}}/src/{{name}}_app.erl"}. +{template, "sup.erl", "{{name}}/src/{{name}}_sup.erl"}. +{template, "otp_app.app.src", "{{name}}/src/{{name}}.app.src"}. +{template, "rebar.config", "{{name}}/rebar.config"}. +{template, "gitignore", "{{name}}/.gitignore"}. +{template, "LICENSE", "{{name}}/LICENSE"}. +{template, "README.md", "{{name}}/README.md"}. diff --git a/priv/templates/cmake.template b/priv/templates/cmake.template index b3f23e8..2874c8a 100644 --- a/priv/templates/cmake.template +++ b/priv/templates/cmake.template @@ -1,2 +1,2 @@ {description, "Makefile for building C/C++ in c_src"}. -{template, "Makefile.dtl", "c_src/Makefile"}. +{template, "Makefile", "c_src/Makefile"}. diff --git a/priv/templates/escript.template b/priv/templates/escript.template new file mode 100644 index 0000000..c0afd3c --- /dev/null +++ b/priv/templates/escript.template @@ -0,0 +1,11 @@ +{description, "Escriptized application"}. +{variables, [ + {name, "mylib", "Name of the OTP application to be escriptized"}, + {desc, "An escript", "Short description of the project"} +]}. +{template, "escript_mod.erl", "{{name}}/src/{{name}}.erl"}. +{template, "otp_lib.app.src", "{{name}}/src/{{name}}.app.src"}. +{template, "escript_rebar.config", "{{name}}/rebar.config"}. +{template, "gitignore", "{{name}}/.gitignore"}. +{template, "LICENSE", "{{name}}/LICENSE"}. +{template, "escript_README.md", "{{name}}/README.md"}. diff --git a/priv/templates/escript_README.md b/priv/templates/escript_README.md new file mode 100644 index 0000000..577d679 --- /dev/null +++ b/priv/templates/escript_README.md @@ -0,0 +1,14 @@ +{{name}} +===== + +{{desc}} + +Build +----- + + $ rebar3 escriptize + +Run +--- + + $ _build/default/bin/{{name}} diff --git a/priv/templates/escript_mod.erl b/priv/templates/escript_mod.erl new file mode 100644 index 0000000..f8a779b --- /dev/null +++ b/priv/templates/escript_mod.erl @@ -0,0 +1,17 @@ +-module({{name}}). + +%% API exports +-export([main/1]). + +%%==================================================================== +%% API functions +%%==================================================================== + +%% escript Entry point +main(Args) -> + io:format("Args: ~p~n", [Args]), + erlang:halt(0). + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/priv/templates/escript_rebar.config b/priv/templates/escript_rebar.config new file mode 100644 index 0000000..196f835 --- /dev/null +++ b/priv/templates/escript_rebar.config @@ -0,0 +1,13 @@ +{erl_opts, [no_debug_info]}. +{deps, []}. + +{escript_incl_apps, + [{{name}}]}. +{escript_top_level_app, {{name}}}. +{escript_name, {{name}}}. +{escript_emu_args, "%%! +sbtu +A0\n"}. + +%% Profiles +{profiles, [{test, + [{erl_opts, [debug_info]} + ]}]}. diff --git a/priv/templates/gitignore.dtl b/priv/templates/gitignore index 40a1d4f..40a1d4f 100644 --- a/priv/templates/gitignore.dtl +++ b/priv/templates/gitignore diff --git a/priv/templates/lib.template b/priv/templates/lib.template index 4b9a75d..cbb3672 100644 --- a/priv/templates/lib.template +++ b/priv/templates/lib.template @@ -3,9 +3,9 @@ {name, "mylib", "Name of the OTP library application"}, {desc, "An OTP library", "Short description of the app"} ]}. -{template, "mod.erl.dtl", "{{name}}/src/{{name}}.erl"}. -{template, "otp_lib.app.src.dtl", "{{name}}/src/{{name}}.app.src"}. -{template, "rebar.config.dtl", "{{name}}/rebar.config"}. -{template, "gitignore.dtl", "{{name}}/.gitignore"}. -{template, "LICENSE.dtl", "{{name}}/LICENSE"}. -{template, "README.md.dtl", "{{name}}/README.md"}. +{template, "mod.erl", "{{name}}/src/{{name}}.erl"}. +{template, "otp_lib.app.src", "{{name}}/src/{{name}}.app.src"}. +{template, "rebar.config", "{{name}}/rebar.config"}. +{template, "gitignore", "{{name}}/.gitignore"}. +{template, "LICENSE", "{{name}}/LICENSE"}. +{template, "README.md", "{{name}}/README.md"}. diff --git a/priv/templates/mod.erl.dtl b/priv/templates/mod.erl index 2f5e09e..2f5e09e 100644 --- a/priv/templates/mod.erl.dtl +++ b/priv/templates/mod.erl diff --git a/priv/templates/otp_app.app.src.dtl b/priv/templates/otp_app.app.src index 5188f56..5188f56 100644 --- a/priv/templates/otp_app.app.src.dtl +++ b/priv/templates/otp_app.app.src diff --git a/priv/templates/otp_lib.app.src.dtl b/priv/templates/otp_lib.app.src index 3adefeb..3adefeb 100644 --- a/priv/templates/otp_lib.app.src.dtl +++ b/priv/templates/otp_lib.app.src diff --git a/priv/templates/plugin.erl.dtl b/priv/templates/plugin.erl index abf4648..abf4648 100644 --- a/priv/templates/plugin.erl.dtl +++ b/priv/templates/plugin.erl diff --git a/priv/templates/plugin.template b/priv/templates/plugin.template index 3419191..0181bd2 100644 --- a/priv/templates/plugin.template +++ b/priv/templates/plugin.template @@ -3,9 +3,9 @@ {name, "myplugin", "Name of the plugin"}, {desc, "A rebar plugin", "Short description of the plugin's purpose"} ]}. -{template, "plugin.erl.dtl", "{{name}}/src/{{name}}.erl"}. -{template, "otp_lib.app.src.dtl", "{{name}}/src/{{name}}.app.src"}. -{template, "rebar.config.dtl", "{{name}}/rebar.config"}. -{template, "gitignore.dtl", "{{name}}/.gitignore"}. -{template, "LICENSE.dtl", "{{name}}/LICENSE"}. -{template, "plugin_README.md.dtl", "{{name}}/README.md"}. +{template, "plugin.erl", "{{name}}/src/{{name}}.erl"}. +{template, "otp_lib.app.src", "{{name}}/src/{{name}}.app.src"}. +{template, "rebar.config", "{{name}}/rebar.config"}. +{template, "gitignore", "{{name}}/.gitignore"}. +{template, "LICENSE", "{{name}}/LICENSE"}. +{template, "plugin_README.md", "{{name}}/README.md"}. diff --git a/priv/templates/plugin_README.md.dtl b/priv/templates/plugin_README.md index 7f9ba84..7f9ba84 100644 --- a/priv/templates/plugin_README.md.dtl +++ b/priv/templates/plugin_README.md diff --git a/priv/templates/rebar.config.dtl b/priv/templates/rebar.config index f618f3e..f618f3e 100644 --- a/priv/templates/rebar.config.dtl +++ b/priv/templates/rebar.config diff --git a/priv/templates/release.template b/priv/templates/release.template index e350130..a15d163 100644 --- a/priv/templates/release.template +++ b/priv/templates/release.template @@ -3,12 +3,12 @@ {name, "myapp", "Name of the OTP release. An app with this name will also be created."}, {desc, "An OTP application", "Short description of the release's main app's purpose"} ]}. -{template, "app.erl.dtl", "{{name}}/{{apps_dir}}/{{name}}/src/{{name}}_app.erl"}. -{template, "sup.erl.dtl", "{{name}}/{{apps_dir}}/{{name}}/src/{{name}}_sup.erl"}. -{template, "otp_app.app.src.dtl", "{{name}}/{{apps_dir}}/{{name}}/src/{{name}}.app.src"}. -{template, "relx_rebar.config.dtl", "{{name}}/rebar.config"}. -{template, "sys.config.dtl", "{{name}}/config/sys.config"}. -{template, "vm.args.dtl", "{{name}}/config/vm.args"}. -{template, "gitignore.dtl", "{{name}}/.gitignore"}. -{template, "LICENSE.dtl", "{{name}}/LICENSE"}. -{template, "README.md.dtl", "{{name}}/README.md"}. +{template, "app.erl", "{{name}}/{{apps_dir}}/{{name}}/src/{{name}}_app.erl"}. +{template, "sup.erl", "{{name}}/{{apps_dir}}/{{name}}/src/{{name}}_sup.erl"}. +{template, "otp_app.app.src", "{{name}}/{{apps_dir}}/{{name}}/src/{{name}}.app.src"}. +{template, "relx_rebar.config", "{{name}}/rebar.config"}. +{template, "sys.config", "{{name}}/config/sys.config"}. +{template, "vm.args", "{{name}}/config/vm.args"}. +{template, "gitignore", "{{name}}/.gitignore"}. +{template, "LICENSE", "{{name}}/LICENSE"}. +{template, "README.md", "{{name}}/README.md"}. diff --git a/priv/templates/relx_rebar.config.dtl b/priv/templates/relx_rebar.config index e7505eb..e7505eb 100644 --- a/priv/templates/relx_rebar.config.dtl +++ b/priv/templates/relx_rebar.config diff --git a/priv/templates/sup.erl.dtl b/priv/templates/sup.erl index a2e7209..a2e7209 100644 --- a/priv/templates/sup.erl.dtl +++ b/priv/templates/sup.erl diff --git a/priv/templates/sys.config.dtl b/priv/templates/sys.config index 7fd6bcb..7fd6bcb 100644 --- a/priv/templates/sys.config.dtl +++ b/priv/templates/sys.config diff --git a/priv/templates/vm.args.dtl b/priv/templates/vm.args index a8a43f0..a8a43f0 100644 --- a/priv/templates/vm.args.dtl +++ b/priv/templates/vm.args diff --git a/rebar.config b/rebar.config index 83da0e5..5eaf6de 100644 --- a/rebar.config +++ b/rebar.config @@ -8,63 +8,64 @@ {providers, "", {git, "https://github.com/tsloughter/providers.git", {tag, "v1.3.1"}}}, - {erlydtl, "", - {git, "https://github.com/erlydtl/erlydtl.git", - {branch, "master"}}}, {relx, "", {git, "https://github.com/erlware/relx.git", - {branch, "master"}}}, + {tag, "v2.0.0"}}}, + {mustache, ".*", + {git, "https://github.com/soranoba/mustache.git", + {tag, "v0.3.0"}}}, {getopt, "", {git, "https://github.com/jcomellas/getopt.git", {branch, "master"}}}]}. {escript_incl_apps, - [getopt, merl, erlydtl, erlware_commons, relx, providers, rebar]}. + [getopt, erlware_commons, relx, providers, rebar]}. {escript_top_level_app, rebar}. {escript_name, rebar3}. {escript_emu_args, "%%! +sbtu +A0\n"}. %% escript_incl_extra is for internal rebar-private use only. %% Do not use outside rebar. Config interface is not stable. -{escript_incl_extra, [{"priv/templates/*", "."}, {"rebar/include/*", "."}]}. +{escript_incl_extra, [{"_build/default/lib/relx/priv/templates/*", "."}, + {"priv/templates/*", "."}, + {"rebar/include/*", "."}]}. {erl_opts, [{platform_define, "R14", no_callback_support}, {platform_define, "^[0-9]+", namespaced_types}, {platform_define, "^R1[4|5]", deprecated_crypto}, - debug_info, + no_debug_info, warnings_as_errors]}. -{erlydtl_opts, [{doc_root, "priv/templates"}, - {compiler_options, [report, return, debug_info]}]}. - -{dialyzer_plt_apps, [common_test, dialyzer, erlydtl, eunit, snmp]}. - -{provider_hooks, [{post, [{compile, {erlydtl, compile}}]}]}. +{dialyzer_plt_apps, [common_test, dialyzer, eunit, snmp]}. %% Profiles {profiles, [{test, [{deps, [ {meck, "", {git, "https://github.com/eproxus/meck.git", {tag, "0.8.2"}}} - ]} - ] - }, + ]}, + {erl_opts, [debug_info]} + ]}, %% We don't want erlydtl to attempt to run on the first compile pass to bootstrap - {bootstrap, [{overrides, [{override, relx, [{provider_hooks, [{post, []}]}]}]}, - {provider_hooks, [{post, []}]}]} + {bootstrap, []} ]}. %% Overrides -{overrides, [{override, erlware_commons, [{plugins, []}]}, - {override, merl, [{pre_hooks, [{"(linux|darwin|solaris)", compile, "make -C \"$REBAR_DEPS_DIR/merl\" all -W test"}, - {"(freebsd|netbsd|openbsd)", compile, "gmake -C \"$REBAR_DEPS_DIR/merl\" all"}, - {"win32", compile, "make -C \"%REBAR_DEPS_DIR%/merl\" all -W test"}, - {eunit, - "erlc -I include/erlydtl_preparser.hrl -o test" - " test/erlydtl_extension_testparser.yrl"}, - {"(linux|darwin|solaris)", eunit, "make -C \"$REBAR_DEPS_DIR/merl\" test"}, - {"(freebsd|netbsd|openbsd)", eunit, "gmake -C \"$REBAR_DEPS_DIR/merl\" test"}, - {"win32", eunit, "make -C \"%REBAR_DEPS_DIR%/merl\" test"} - ]}]}, - {override, erlydtl, [{pre_hooks, []}]} +{overrides, [{override, erlware_commons, [ + {erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + {platform_define, "^R1[4|5]", deprecated_crypto}, + no_debug_info, + warnings_as_errors]}, + {deps, []}, {plugins, []} + ]}, + {override, mustache, [ + {erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + no_debug_info]}, + {deps, []}, {plugins, []}]}, + {override, getopt, [{erl_opts, [no_debug_info]}]}, + {override, providers, [{erl_opts, [no_debug_info]}]}, + {override, relx, [{erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, + {platform_define, "^R1[4|5]", deprecated_crypto}, + no_debug_info, + warnings_as_errors]}]} ]}. @@ -1,31 +1,19 @@ -[{<<"rebar_vsn_plugin">>, - {git,"https://github.com/erlware/rebar_vsn_plugin.git", - {ref,"fd40c960c7912193631d948fe962e1162a8d1334"}}, - 1}, - {<<"merl">>, - {git,"git://github.com/erlydtl/merl.git", - {ref,"750b09d44425f435ff579a4d28bf5844bb5b4ef1"}}, - 1}, - {<<"eunit_formatters">>, - {git,"git://github.com/seancribbs/eunit_formatters", - {ref,"2c73eb6e46b0863f19507857b386a48a53aaf141"}}, - 1}, - {<<"relx">>, +[{<<"relx">>, {git,"https://github.com/erlware/relx.git", - {ref,"f8af9f5cd9f70ff43a043fe274dda4e56be82a2d"}}, + {ref,"2e59b1c95575b3c104cc191e954c82baadc43c6c"}}, 0}, {<<"providers">>, {git,"https://github.com/tsloughter/providers.git", {ref,"7563ba7e916d5a35972b25b3aa1945ffe0a8e7a5"}}, 0}, + {<<"mustache">>, + {git,"https://github.com/soranoba/mustache.git", + {ref,"e5401042c66039eef20ee81abc1537ced1f22bc7"}}, + 0}, {<<"getopt">>, {git,"https://github.com/jcomellas/getopt.git", {ref,"626698975e63866156159661d100785d65eab6f9"}}, 0}, - {<<"erlydtl">>, - {git,"https://github.com/erlydtl/erlydtl.git", - {ref,"a4ac28680d6e066aabf86b3be9f073352a1a4d40"}}, - 0}, {<<"erlware_commons">>, {git,"https://github.com/erlware/erlware_commons.git", {ref,"ef0d252b11c863f9c228af2fe93a4e42fba2f7f3"}}, diff --git a/src/rebar.app.src b/src/rebar.app.src index b404c42..f753784 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -3,7 +3,7 @@ {application, rebar, [{description, "Rebar: Erlang Build Tool"}, - {vsn, "3.0.0-alpha-4"}, + {vsn, "3.0.0-alpha-5"}, {modules, []}, {registered, []}, {applications, [kernel, @@ -37,7 +37,6 @@ rebar_prv_dialyzer, rebar_prv_do, rebar_prv_edoc, - rebar_prv_erlydtl_compiler, rebar_prv_escriptize, rebar_prv_eunit, rebar_prv_help, diff --git a/src/rebar3.erl b/src/rebar3.erl index b0cc949..92803c5 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -127,19 +127,17 @@ run_aux(State, RawArgs) -> filename:join(filename:absname(rebar_state:dir(State2)), BaseDir)), {ok, Providers} = application:get_env(rebar, providers), - {ok, Resources} = application:get_env(rebar, resources), - State4 = rebar_state:resources(State3, Resources), - State5 = rebar_plugins:install(State4), + State4 = rebar_plugins:install(State3), %% Providers can modify profiles stored in opts, so set default after initializing providers - State6 = rebar_state:create_logic_providers(Providers, State5), - State7 = rebar_state:default(State6, rebar_state:opts(State6)), + State5 = rebar_state:create_logic_providers(Providers, State4), + State6 = rebar_state:default(State5, rebar_state:opts(State5)), {Task, Args} = parse_args(RawArgs), - State8 = rebar_state:code_paths(State7, default, code:get_path()), + State7 = rebar_state:code_paths(State6, default, code:get_path()), - rebar_core:init_command(rebar_state:command_args(State8, Args), Task). + rebar_core:init_command(rebar_state:command_args(State7, Args), Task). init_config() -> %% Initialize logging system diff --git a/src/rebar_api.erl b/src/rebar_api.erl index 88c7538..45083df 100644 --- a/src/rebar_api.erl +++ b/src/rebar_api.erl @@ -10,7 +10,8 @@ get_arch/0, wordsize/0, add_deps_to_path/1, - restore_code_path/1]). + restore_code_path/1, + processing_base_dir/1]). -export_type([rebar_dict/0, rebar_digraph/0]). @@ -59,3 +60,6 @@ add_deps_to_path(State) -> %% Revert to only having the beams necessary for running rebar3 and plugins in the path restore_code_path(State) -> rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)). + +processing_base_dir(State) -> + rebar_dir:processing_base_dir(State). diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index 41f41f5..73401bc 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -159,7 +159,12 @@ find_app(AppDir, Validate) -> case Validate of V when V =:= invalid ; V =:= all -> AppInfo = create_app_info(AppDir, File), - {true, rebar_app_info:app_file_src(AppInfo, File)}; + case AppInfo of + {error, Reason} -> + throw({error, {invalid_app_file, File, Reason}}); + _ -> + {true, rebar_app_info:app_file_src(AppInfo, File)} + end; valid -> false end; @@ -175,7 +180,7 @@ find_app(AppDir, Validate) -> app_dir(AppFile) -> filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))). --spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | error. +-spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}. create_app_info(AppDir, AppFile) -> case file:consult(AppFile) of {ok, [{application, AppName, AppDetails}]} -> @@ -193,8 +198,8 @@ create_app_info(AppDir, AppFile) -> false end, rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir); - _ -> - error + {error, Reason} -> + {error, Reason} end. dedup([]) -> []; diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index c0c6bb2..a94c72d 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -34,7 +34,7 @@ profile_dir(State, Profiles) -> ["default"] -> ["default"]; %% drop `default` from the profile dir if it's implicit and reverse order %% of profiles to match order passed to `as` - ["default"|Rest] -> lists:reverse(Rest) + ["default"|Rest] -> Rest end, ProfilesDir = string:join(ProfilesStrings, "+"), filename:join(rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ProfilesDir). diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 862d21e..8fe03b7 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -161,7 +161,7 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) -> filename:extension(File) =:= ".erl"], NeededErlFiles = needed_files(G, ErlOpts, Dir, OutDir1, AllErlFiles), - ErlFirstFiles = erl_first_files(Config, Dir, NeededErlFiles), + {ErlFirstFiles, ErlOptsFirst} = erl_first_files(Config, ErlOpts, Dir, NeededErlFiles), {DepErls, OtherErls} = lists:partition( fun(Source) -> digraph:in_degree(G, Source) > 0 end, [File || File <- NeededErlFiles, not lists:member(File, ErlFirstFiles)]), @@ -171,15 +171,36 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) -> rebar_base_compiler:run( Config, FirstErls, OtherErls, fun(S, C) -> - internal_erl_compile(C, Dir, S, OutDir1, ErlOpts) + ErlOpts1 = case lists:member(S, ErlFirstFiles) of + true -> ErlOptsFirst; + false -> ErlOpts + end, + internal_erl_compile(C, Dir, S, OutDir1, ErlOpts1) end), ok. -erl_first_files(Config, Dir, NeededErlFiles) -> +%% Get files which need to be compiled first, i.e. those specified in erl_first_files +%% and parse_transform options. Also produce specific erl_opts for these first +%% files, so that yet to be compiled parse transformations are excluded from it. +erl_first_files(Config, ErlOpts, Dir, NeededErlFiles) -> ErlFirstFilesConf = rebar_state:get(Config, erl_first_files, []), - %% NOTE: order of files in ErlFirstFiles is important! - [filename:join(Dir, File) || File <- ErlFirstFilesConf, - lists:member(filename:join(Dir, File), NeededErlFiles)]. + NeededSrcDirs = lists:usort(lists:map(fun filename:dirname/1, NeededErlFiles)), + %% NOTE: order of files here is important! + ErlFirstFiles = + [filename:join(Dir, File) || File <- ErlFirstFilesConf, + lists:member(filename:join(Dir, File), NeededErlFiles)], + {ParseTransforms, ParseTransformsErls} = + lists:unzip(lists:flatmap( + fun(PT) -> + PTerls = [filename:join(D, module_to_erl(PT)) || D <- NeededSrcDirs], + [{PT, PTerl} || PTerl <- PTerls, lists:member(PTerl, NeededErlFiles)] + end, proplists:get_all_values(parse_transform, ErlOpts))), + ErlOptsFirst = lists:filter(fun({parse_transform, PT}) -> + not lists:member(PT, ParseTransforms); + (_) -> + true + end, ErlOpts), + {ErlFirstFiles ++ ParseTransformsErls, ErlOptsFirst}. %% Get subset of SourceFiles which need to be recompiled, respecting %% dependencies induced by given graph G. diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index 3b77da2..2d83579 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -16,7 +16,7 @@ lock(AppDir, {git, Url, _}) -> lock(AppDir, {git, Url}) -> AbortMsg = io_lib:format("Locking of git dependency failed in ~s", [AppDir]), {ok, VsnString} = - rebar_utils:sh("git --git-dir='" ++ AppDir ++ "/.git' rev-parse --verify HEAD", + rebar_utils:sh("git --git-dir=\"" ++ AppDir ++ "/.git\" rev-parse --verify HEAD", [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]), Ref = string:strip(VsnString, both, $\n), {git, Url, {ref, Ref}}. @@ -31,11 +31,14 @@ needs_update(Dir, {git, Url, {tag, Tag}}) -> ?DEBUG("Comparing git tag ~s with ~s", [Tag, Current1]), not ((Current1 =:= Tag) andalso compare_url(Dir, Url)); needs_update(Dir, {git, Url, {branch, Branch}}) -> - {ok, Current} = rebar_utils:sh(?FMT("git symbolic-ref -q --short HEAD", []), + %% Fetch remote so we can check if the branch has changed + {ok, _} = rebar_utils:sh(?FMT("git fetch origin ~s", [Branch]), + [{cd, Dir}]), + %% Check for new commits to origin/Branch + {ok, Current} = rebar_utils:sh(?FMT("git log HEAD..origin/~s --oneline", [Branch]), [{cd, Dir}]), - Current1 = string:strip(string:strip(Current, both, $\n), both, $\r), - ?DEBUG("Comparing git branch ~s with ~s", [Branch, Current1]), - not ((Current1 =:= Branch) andalso compare_url(Dir, Url)); + ?DEBUG("Checking git branch ~s for updates", [Branch]), + not ((Current =:= []) andalso compare_url(Dir, Url)); needs_update(Dir, {git, Url, "master"}) -> needs_update(Dir, {git, Url, {branch, "master"}}); needs_update(Dir, {git, Url, Ref}) -> @@ -122,7 +125,7 @@ collect_default_refcount() -> %% timestamp is really important from an ordering perspective. AbortMsg1 = "Getting log of git dependency failed in " ++ rebar_dir:get_cwd(), {ok, String} = - rebar_utils:sh("git log -n 1 --pretty=format:'%h\n' ", + rebar_utils:sh("git log -n 1 --pretty=format:\"%h\n\" ", [{use_stdout, false}, {debug_abort_on_error, AbortMsg1}]), RawRef = string:strip(String, both, $\n), @@ -132,41 +135,43 @@ collect_default_refcount() -> case Tag of undefined -> AbortMsg2 = "Getting rev-list of git depedency failed in " ++ rebar_dir:get_cwd(), - rebar_utils:sh("git rev-list HEAD | wc -l", - [{use_stdout, false}, - {debug_abort_on_error, AbortMsg2}]); + {ok, PatchLines} = rebar_utils:sh("git rev-list HEAD", + [{use_stdout, false}, + {debug_abort_on_error, AbortMsg2}]), + rebar_utils:line_count(PatchLines); _ -> get_patch_count(Tag) end, {TagVsn, RawRef, RawCount}. -build_vsn_string(Vsn, RawRef, RawCount) -> +build_vsn_string(Vsn, RawRef, Count) -> %% Cleanup the tag and the Ref information. Basically leading 'v's and %% whitespace needs to go away. RefTag = [".ref", re:replace(RawRef, "\\s", "", [global])], - Count = erlang:iolist_to_binary(re:replace(RawCount, "\\s", "", [global])), %% Create the valid [semver](http://semver.org) version from the tag case Count of - <<"0">> -> + 0 -> erlang:binary_to_list(erlang:iolist_to_binary(Vsn)); _ -> erlang:binary_to_list(erlang:iolist_to_binary([Vsn, "+build.", - Count, RefTag])) + integer_to_list(Count), RefTag])) end. get_patch_count(RawRef) -> AbortMsg = "Getting rev-list of git dep failed in " ++ rebar_dir:get_cwd(), Ref = re:replace(RawRef, "\\s", "", [global]), - Cmd = io_lib:format("git rev-list ~s..HEAD | wc -l", + Cmd = io_lib:format("git rev-list ~s..HEAD", [Ref]), - rebar_utils:sh(Cmd, - [{use_stdout, false}, - {debug_abort_on_error, AbortMsg}]). + {ok, PatchLines} = rebar_utils:sh(Cmd, + [{use_stdout, false}, + {debug_abort_on_error, AbortMsg}]), + rebar_utils:line_count(PatchLines). + parse_tags() -> %% Don't abort on error, we want the bad return to be turned into 0.0.0 - case rebar_utils:sh("git log --oneline --decorate | fgrep \"tag: \" -1000", + case rebar_utils:sh("git log --oneline --no-walk --tags --decorate", [{use_stdout, false}, return_on_error]) of {error, _} -> {undefined, "0.0.0"}; diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl index 260343d..0bf27c9 100644 --- a/src/rebar_otp_app.erl +++ b/src/rebar_otp_app.erl @@ -104,7 +104,7 @@ preprocess(State, AppInfo, AppSrcFile) -> A1 = apply_app_vars(AppVars, AppData), %% AppSrcFile may contain instructions for generating a vsn number - Vsn = app_vsn(AppSrcFile), + Vsn = app_vsn(AppSrcFile, State), A2 = lists:keystore(vsn, 1, A1, {vsn, Vsn}), %% systools:make_relup/4 fails with {missing_param, registered} @@ -197,11 +197,12 @@ consult_app_file(Filename) -> end end. -app_vsn(AppFile) -> +app_vsn(AppFile, State) -> case consult_app_file(AppFile) of {ok, [{application, _AppName, AppData}]} -> AppDir = filename:dirname(filename:dirname(AppFile)), - rebar_utils:vcs_vsn(get_value(vsn, AppData, AppFile), AppDir); + Resources = rebar_state:resources(State), + rebar_utils:vcs_vsn(get_value(vsn, AppData, AppFile), AppDir, Resources); {error, Reason} -> ?ABORT("Failed to consult app file ~s: ~p\n", [AppFile, Reason]) end. diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl index 8982573..4ab5f9f 100644 --- a/src/rebar_packages.erl +++ b/src/rebar_packages.erl @@ -2,6 +2,7 @@ -export([get_packages/1 ,registry/1 + ,check_registry/3 ,find_highest_matching/3]). -export_type([package/0]). @@ -52,6 +53,18 @@ registry(State) -> error end. +check_registry(Pkg, Vsn, State) -> + case rebar_state:registry(State) of + {ok, T} -> + case ets:lookup(T, Pkg) of + [{Pkg, [Vsns]}] -> + lists:member(Vsn, Vsns); + _ -> + false + end; + error -> + false + end. %% Hex supports use of ~> to specify the version required for a dependency. %% Since rebar3 requires exact versions to choose from we find the highest @@ -73,14 +86,14 @@ find_highest_matching(Dep, Constraint, T) -> [{Dep, [[Vsn]]}] -> case ec_semver:pes(Vsn, Constraint) of true -> - Vsn; + {ok, Vsn}; false -> ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. " "Using anyway, but it is not guarenteed to work.", [Dep, Vsn, Constraint]), - Vsn + {ok, Vsn} end; [{Dep, [[HeadVsn | VsnTail]]}] -> - lists:foldl(fun(Version, Highest) -> + {ok, lists:foldl(fun(Version, Highest) -> case ec_semver:pes(Version, Constraint) andalso ec_semver:gt(Version, Highest) of true -> @@ -88,5 +101,8 @@ find_highest_matching(Dep, Constraint, T) -> false -> Highest end - end, HeadVsn, VsnTail) + end, HeadVsn, VsnTail)}; + [] -> + ?WARN("Missing registry entry for package ~s", [Dep]), + none end. diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl index 02e8f4e..c16223e 100644 --- a/src/rebar_plugins.erl +++ b/src/rebar_plugins.erl @@ -42,6 +42,7 @@ handle_plugin(Plugin, State) -> Apps = rebar_state:all_deps(State2), ToBuild = lists:dropwhile(fun rebar_app_info:valid/1, Apps), [build_plugin(AppInfo) || AppInfo <- ToBuild], + [true = code:add_patha(filename:join(rebar_app_info:dir(AppInfo), "ebin")) || AppInfo <- Apps], plugin_providers(Plugin) catch C:T -> @@ -54,8 +55,7 @@ build_plugin(AppInfo) -> AppDir = rebar_app_info:dir(AppInfo), C = rebar_config:consult(AppDir), S = rebar_state:new(rebar_state:new(), C, AppDir), - rebar_prv_compile:compile(S, AppInfo), - true = code:add_patha(filename:join(AppDir, "ebin")). + rebar_prv_compile:compile(S, AppInfo). plugin_providers({Plugin, _, _}) when is_atom(Plugin) -> validate_plugin(Plugin); diff --git a/src/rebar_prv_app_discovery.erl b/src/rebar_prv_app_discovery.erl index 31c0f59..97862c1 100644 --- a/src/rebar_prv_app_discovery.erl +++ b/src/rebar_prv_app_discovery.erl @@ -45,5 +45,13 @@ do(State) -> -spec format_error(any()) -> iolist(). format_error({multiple_app_files, Files}) -> io_lib:format("Multiple app files found in one app dir: ~s", [string:join(Files, " and ")]); +format_error({invalid_app_file, File, Reason}) -> + case Reason of + {Line, erl_parse, Description} -> + io_lib:format("Invalid app file ~s at line ~b: ~p", + [File, Line, lists:flatten(Description)]); + _ -> + io_lib:format("Invalid app file ~s: ~p", [File, Reason]) + end; format_error(Reason) -> io_lib:format("~p", [Reason]). diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 9038759..d4370de 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -280,23 +280,25 @@ find_suite_dirs(Suites) -> %% eliminate duplicates lists:usort(AllDirs). -copy(State, Target) -> - case reduce_path(Target) == rebar_state:dir(State) of +copy(State, Dir) -> + From = reduce_path(Dir), + case From == rebar_state:dir(State) of true -> throw({error, suite_at_project_root}); false -> ok end, - case retarget_path(State, Target) of + case retarget_path(State, From) of %% directory lies outside of our project's file structure so %% don't copy it - Target -> Target; - NewTarget -> - %% unlink the directory if it's a symlink - case ec_file:is_symlink(NewTarget) of - true -> ok = ec_file:remove(NewTarget); + From -> From; + Target -> + %% recursively delete any symlinks in the target directory + %% if it exists so we don't smash files in the linked dirs + case ec_file:is_dir(Target) of + true -> remove_links(Target); false -> ok end, - ok = ec_file:copy(Target, NewTarget, [recursive]), - NewTarget + ok = ec_file:copy(From, Target, [recursive]), + Target end. compile_dir(State, Dir, OutDir) -> @@ -343,6 +345,26 @@ reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest); reduce_path([], [".."|Rest]) -> reduce_path([], Rest); reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest). +remove_links(Path) -> + case ec_file:is_dir(Path) of + false -> ok; + true -> remove_links1(Path) + end. + +remove_links1(Path) -> + case ec_file:is_symlink(Path) of + true -> + file:delete(Path); + false -> + lists:foreach(fun(ChildPath) -> + remove_links(ChildPath) + end, sub_dirs(Path)) + end. + +sub_dirs(Path) -> + {ok, SubDirs} = file:list_dir(Path), + [filename:join(Path, SubDir) || SubDir <- SubDirs]. + replace_src_dirs(State, Dirs) -> %% replace any `src_dirs` with the test dirs ErlOpts = rebar_state:get(State, erl_opts, []), diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl index 3627e91..be81c31 100644 --- a/src/rebar_prv_deps.erl +++ b/src/rebar_prv_deps.erl @@ -13,14 +13,18 @@ -spec init(rebar_state:t()) -> {ok, rebar_state:t()}. init(State) -> - State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER}, - {module, ?MODULE}, - {bare, true}, - {deps, ?DEPS}, - {example, "rebar3 deps"}, - {short_desc, "List dependencies"}, - {desc, info("List dependencies")}, - {opts, []}])), + State1 = rebar_state:add_provider( + State, + providers:create([ + {name, ?PROVIDER}, + {module, ?MODULE}, + {bare, true}, + {deps, ?DEPS}, + {example, "rebar3 deps"}, + {short_desc, "List dependencies"}, + {desc, "List dependencies. Those not matching lock files " + "are followed by an asterisk (*)."}, + {opts, []}])), {ok, State1}. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. @@ -44,35 +48,55 @@ display(State, Profile, Deps) -> display_deps(State, Deps), ?CONSOLE("", []). -merge(Deps, Deps) -> - Deps; merge(Deps, SourceDeps) -> + merge1(dedup([normalize(Dep) || Dep <- Deps]), + [normalize(Dep) || Dep <- SourceDeps]). + +normalize(Name) when is_binary(Name) -> + Name; +normalize(Name) when is_atom(Name) -> + ec_cnv:to_binary(Name); +normalize(Dep) when is_tuple(Dep) -> + Name = element(1, Dep), + setelement(1, Dep, normalize(Name)). + +merge1(Deps, SourceDeps) -> + Names = [name(Dep) || Dep <- Deps], ToAdd = [Dep || Dep <- SourceDeps, - not lists:keymember(ec_cnv:to_binary(element(1,Dep)), 1, Deps)], + not lists:member(name(Dep), Names)], Deps ++ ToAdd. +%% Keep the latter one as locks come after regular deps in the list. +%% This is totally not safe as an assumption, but it's what we got. +%% We do this by comparing the current element and looking if a +%% similar named one happens later. If so, drop the current one. +dedup(Deps) -> dedup(Deps, [name(Dep) || Dep <- Deps]). + +dedup([], []) -> []; +dedup([Dep|Deps], [Name|DepNames]) -> + case lists:member(Name, DepNames) of + true -> dedup(Deps, DepNames); + false -> [Dep | dedup(Deps, DepNames)] + end. + +name(T) when is_tuple(T) -> element(1, T); +name(B) when is_binary(B) -> B. + + display_deps(State, Deps) -> lists:foreach(fun(Dep) -> display_dep(State, Dep) end, Deps). %% packages display_dep(_State, {Name, Vsn}) when is_list(Vsn) -> ?CONSOLE("~s* (package ~s)", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]); -display_dep(_State, Name) when is_atom(Name) -> - ?CONSOLE("~s* (package)", [ec_cnv:to_binary(Name)]); -%% git source -display_dep(_State, {Name, Source}) when is_tuple(Source), element(1, Source) =:= git -> - ?CONSOLE("~s* (git source)", [ec_cnv:to_binary(Name)]); -display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source), element(1, Source) =:= git -> - ?CONSOLE("~s* (git source)", [ec_cnv:to_binary(Name)]); -display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source), element(1, Source) =:= git -> - ?CONSOLE("~s* (git soutce)", [ec_cnv:to_binary(Name)]); -%% unknown source +display_dep(_State, Name) when is_binary(Name) -> + ?CONSOLE("~s* (package)", [Name]); display_dep(_State, {Name, Source}) when is_tuple(Source) -> - ?CONSOLE("~s* (source ~p)", [ec_cnv:to_binary(Name), Source]); + ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) -> - ?CONSOLE("~s* (source ~p)", [ec_cnv:to_binary(Name), Source]); + ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) -> - ?CONSOLE("~s* (source ~p)", [ec_cnv:to_binary(Name), Source]); + ?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]); %% Locked display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) -> DepsDir = rebar_dir:deps_dir(State), @@ -82,14 +106,6 @@ display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) - false -> "" end, ?CONSOLE("~s~s (locked package ~s)", [Name, NeedsUpdate, Vsn]); -display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Level), element(1, Source) =:= git -> - DepsDir = rebar_dir:deps_dir(State), - AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]), - NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of - true -> "*"; - false -> "" - end, - ?CONSOLE("~s~s (locked git source)", [Name, NeedsUpdate]); display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Level) -> DepsDir = rebar_dir:deps_dir(State), AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]), @@ -97,39 +113,6 @@ display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Leve true -> "*"; false -> "" end, - ?CONSOLE("~s~s (locked ~p)", [Name, NeedsUpdate, Source]). - -info(Description) -> - io_lib:format("~s.~n" - "~n" - "Valid rebar.config options:~n" - " ~p~n" - " ~p~n" - "Valid command line options:~n" - " deps_dir=\"deps\" (override default or rebar.config deps_dir)~n", - [ - Description, - {deps_dir, "deps"}, - {deps, - [app_name, - {rebar, "1.0.*"}, - {rebar, ".*", - {git, "git://github.com/rebar/rebar.git"}}, - {rebar, ".*", - {git, "git://github.com/rebar/rebar.git", "Rev"}}, - {rebar, "1.0.*", - {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, - {rebar, "1.0.0", - {git, "git://github.com/rebar/rebar.git", {tag, "1.0.0"}}}, - {rebar, "", - {git, "git://github.com/rebar/rebar.git", {branch, "master"}}, - [raw]}, - {app_name, ".*", {hg, "https://www.example.org/url"}}, - {app_name, ".*", {rsync, "Url"}}, - {app_name, ".*", {svn, "https://www.example.org/url"}}, - {app_name, ".*", {svn, "svn://svn.example.org/url"}}, - {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}}, - {app_name, ".*", {fossil, "https://www.example.org/url"}}, - {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}, - {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]} - ]). + ?CONSOLE("~s~s (locked ~s source)", [Name, NeedsUpdate, type(Source)]). + +type(Source) when is_tuple(Source) -> element(1, Source). diff --git a/src/rebar_prv_erlydtl_compiler.erl b/src/rebar_prv_erlydtl_compiler.erl deleted file mode 100644 index a7442af..0000000 --- a/src/rebar_prv_erlydtl_compiler.erl +++ /dev/null @@ -1,270 +0,0 @@ -%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- -%% ex: ts=4 sw=4 et -%% ------------------------------------------------------------------- -%% -%% rebar: Erlang Build Tools -%% -%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com), -%% Bryan Fink (bryan@basho.com) -%% -%% Permission is hereby granted, free of charge, to any person obtaining a copy -%% of this software and associated documentation files (the "Software"), to deal -%% in the Software without restriction, including without limitation the rights -%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -%% copies of the Software, and to permit persons to whom the Software is -%% furnished to do so, subject to the following conditions: -%% -%% The above copyright notice and this permission notice shall be included in -%% all copies or substantial portions of the Software. -%% -%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -%% THE SOFTWARE. -%% ------------------------------------------------------------------- - -%% The rebar_erlydtl_compiler module is a plugin for rebar that compiles -%% ErlyDTL templates. By default, it compiles all templates/*.dtl -%% to ebin/*_dtl.beam. -%% -%% Configuration options should be placed in rebar.config under -%% 'erlydtl_opts'. It can be a list of name-value tuples or a list of -%% lists of name-value tuples if you have multiple template directories -%% that need to have different settings (see example below). -%% -%% Available options include: -%% -%% doc_root: where to find templates to compile -%% "templates" by default -%% -%% out_dir: where to put compiled template beam files -%% "ebin" by default -%% -%% source_ext: the file extension the template sources have -%% ".dtl" by default -%% -%% module_ext: characters to append to the template's module name -%% "_dtl" by default -%% -%% recursive: boolean that determines if doc_root(s) need to be -%% scanned recursively for matching template file names -%% (default: true). -%% For example, if you had: -%% /t_src/ -%% base.html -%% foo.html -%% -%% And you wanted them compiled to: -%% /priv/ -%% base.beam -%% foo.beam -%% -%% You would add to your rebar.config: -%% {erlydtl_opts, [ -%% {doc_root, "t_src"}, -%% {out_dir, "priv"}, -%% {source_ext, ".html"}, -%% {module_ext, ""} -%% ]}. -%% -%% The default settings are the equivalent of: -%% {erlydtl_opts, [ -%% {doc_root, "templates"}, -%% {out_dir, "ebin"}, -%% {source_ext, ".dtl"}, -%% {module_ext, "_dtl"} -%% ]}. -%% -%% The following example will compile the following templates: -%% "src/*.dtl" files into "ebin/*_dtl.beam" and -%% "templates/*.html" into "ebin/*.beam". Note that any tuple option -%% (such as 'out_dir') in the outer list is added to each inner list: -%% {erlydtl_opts, [ -%% {out_dir, "ebin"}, -%% {recursive, false}, -%% [ -%% {doc_root, "src"}, {module_ext, "_dtl"} -%% ], -%% [ -%% {doc_root, "templates"}, {module_ext, ""}, {source_ext, ".html"} -%% ] -%% ]}. --module(rebar_prv_erlydtl_compiler). - --behaviour(provider). - --export([init/1, - do/1, - format_error/1]). - --include("rebar.hrl"). --include_lib("providers/include/providers.hrl"). - --define(PROVIDER, compile). --define(DEPS, [{default, compile}]). - -%% =================================================================== -%% Public API -%% =================================================================== - --spec init(rebar_state:t()) -> {ok, rebar_state:t()}. -init(State) -> - State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER}, - {module, ?MODULE}, - {namespace, erlydtl}, - {bare, false}, - {deps, ?DEPS}, - {example, "rebar3 erlydtl compile"}, - {short_desc, "Compile erlydtl templates."}, - {desc, "Compile erlydtl templates."}, - {opts, []}])), - {ok, State1}. - -do(State) -> - ?INFO("Running erlydtl...", []), - case rebar_state:get(State, escript_main_app, undefined) of - undefined -> - Dir = rebar_state:dir(State), - case rebar_app_discover:find_app(Dir, all) of - {true, AppInfo} -> - AllApps = rebar_state:project_apps(State) ++ rebar_state:all_deps(State), - case rebar_app_utils:find(rebar_app_info:name(AppInfo), AllApps) of - {ok, AppInfo1} -> - %% Use the existing app info instead of newly created one - run_erlydtl(AppInfo1, State); - _ -> - run_erlydtl(AppInfo, State) - end, - {ok, State}; - _ -> - ?PRV_ERROR(no_main_app) - end; - Name -> - AllApps = rebar_state:project_apps(State) ++ rebar_state:all_deps(State), - {ok, App} = rebar_app_utils:find(Name, AllApps), - run_erlydtl(App, State), - {ok, State} - end. - -run_erlydtl(App, State) -> - Dir = rebar_state:dir(State), - DtlOpts = proplists:unfold(rebar_state:get(State, erlydtl_opts, [])), - TemplateDir = filename:join(Dir, option(doc_root, DtlOpts)), - DtlOpts2 = [{doc_root, TemplateDir} | proplists:delete(doc_root, DtlOpts)], - OutDir = rebar_app_info:ebin_dir(App), - filelib:ensure_dir(filename:join(OutDir, "dummy.beam")), - rebar_base_compiler:run(State, - [], - TemplateDir, - option(source_ext, DtlOpts2), - OutDir, - option(module_ext, DtlOpts2) ++ ".beam", - fun(S, T, C) -> - compile_dtl(C, S, T, DtlOpts2, Dir, OutDir) - end, - [{check_last_mod, false}, - {recursive, option(recursive, DtlOpts2)}]). - --spec format_error(any()) -> iolist(). -format_error(no_main_app) -> - "Erlydtl Error: Multiple project apps found and no {app, atom()} option found in erlydtl_opts."; -format_error(Reason) -> - io_lib:format("~p", [Reason]). - -%% =================================================================== -%% Internal functions -%% =================================================================== - -option(Opt, DtlOpts) -> - proplists:get_value(Opt, DtlOpts, default(Opt)). - -default(app) -> undefined; -default(doc_root) -> "priv/templates"; -default(source_ext) -> ".dtl"; -default(module_ext) -> "_dtl"; -default(custom_tags_dir) -> ""; -default(compiler_options) -> [return]; -default(recursive) -> true. - -compile_dtl(State, Source, Target, DtlOpts, Dir, OutDir) -> - case needs_compile(Source, Target, DtlOpts) of - true -> - do_compile(State, Source, Target, DtlOpts, Dir, OutDir); - false -> - skipped - end. - -do_compile(State, Source, Target, DtlOpts, Dir, OutDir) -> - CompilerOptions = option(compiler_options, DtlOpts), - - Sorted = proplists:unfold( - lists:sort( - [{out_dir, OutDir}, - {doc_root, filename:join(Dir, option(doc_root, DtlOpts))}, - {custom_tags_dir, option(custom_tags_dir, DtlOpts)}, - {compiler_options, CompilerOptions}])), - - %% ensure that doc_root and out_dir are defined, - %% using defaults if necessary - Opts = lists:ukeymerge(1, DtlOpts, Sorted), - ?DEBUG("Compiling \"~s\" -> \"~s\" with options:~n ~s", - [Source, Target, io_lib:format("~p", [Opts])]), - case erlydtl:compile_file(ec_cnv:to_list(Source), - list_to_atom(module_name(Target)), - Opts) of - {ok, _Mod} -> - ok; - {ok, _Mod, Ws} -> - rebar_base_compiler:ok_tuple(State, Source, Ws); - error -> - rebar_base_compiler:error_tuple(State, Source, [], [], Opts); - {error, Es, Ws} -> - rebar_base_compiler:error_tuple(State, Source, Es, Ws, Opts) - end. - -module_name(Target) -> - filename:rootname(filename:basename(Target), ".beam"). - -needs_compile(Source, Target, DtlOpts) -> - LM = filelib:last_modified(Target), - LM < filelib:last_modified(Source) orelse - lists:any(fun(D) -> LM < filelib:last_modified(D) end, - referenced_dtls(Source, DtlOpts)). - -referenced_dtls(Source, DtlOpts) -> - DtlOpts1 = lists:keyreplace(doc_root, 1, DtlOpts, - {doc_root, filename:dirname(Source)}), - Set = referenced_dtls1([Source], DtlOpts1, - sets:add_element(Source, sets:new())), - sets:to_list(sets:del_element(Source, Set)). - -referenced_dtls1(Step, DtlOpts, Seen) -> - ExtMatch = re:replace(option(source_ext, DtlOpts), "\.", "\\\\\\\\.", - [{return, list}]), - - ShOpts = [{use_stdout, false}, return_on_error], - AllRefs = - lists:append( - [begin - Cmd = lists:flatten(["grep -o [^\\\"]*\\", - ExtMatch, "[^\\\"]* ", F]), - case rebar_utils:sh(Cmd, ShOpts) of - {ok, Res} -> - string:tokens(Res, "\n"); - {error, _} -> - "" - end - end || F <- Step]), - DocRoot = option(doc_root, DtlOpts), - WithPaths = [ filename:join([DocRoot, F]) || F <- AllRefs ], - ?DEBUG("All deps: ~p\n", [WithPaths]), - Existing = [F || F <- WithPaths, filelib:is_regular(F)], - New = sets:subtract(sets:from_list(Existing), Seen), - case sets:size(New) of - 0 -> Seen; - _ -> referenced_dtls1(sets:to_list(New), DtlOpts, - sets:union(New, Seen)) - end. diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index e25e2c5..3fb2d71 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -99,6 +99,8 @@ format_error({bad_constraint, Name, Constraint}) -> io_lib:format("Unable to parse version for package ~s: ~s", [Name, Constraint]); format_error({parse_dep, Dep}) -> io_lib:format("Failed parsing dep ~p", [Dep]); +format_error({not_rebar_package, Package, Version}) -> + io_lib:format("Package not buildable with rebar3: ~s-~s", [Package, Version]); format_error({missing_package, Package, Version}) -> io_lib:format("Package not found in registry: ~s-~s", [Package, Version]); format_error({cycles, Cycles}) -> @@ -128,30 +130,39 @@ handle_deps(Profile, State, Deps, Locks) when is_list(Locks) -> handle_deps(_Profile, State, [], _, _) -> {ok, [], State}; handle_deps(Profile, State0, Deps, Upgrade, Locks) -> - %% Read in package index and dep graph - {Packages, Graph} = rebar_state:packages(State0), - Registry = rebar_packages:registry(State0), - State = rebar_state:packages(rebar_state:registry(State0, Registry), {Packages, Graph}), %% Split source deps from pkg deps, needed to keep backwards compatibility - DepsDir = profile_dep_dir(State, Profile), - {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State, Locks, 0), + DepsDir = profile_dep_dir(State0, Profile), + + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, State0, Locks, 0), %% Fetch transitive src deps - {State1, SrcApps, PkgDeps1, Seen} = - update_src_deps(Profile, 0, SrcDeps, PkgDeps, [], State, Upgrade, sets:new(), Locks), + {State1, SrcApps, PkgDeps1, Seen} = update_src_deps(Profile, 0, SrcDeps, PkgDeps, [] + ,State0, Upgrade, sets:new(), Locks), - {Solved, State2} = - update_pkg_deps(Profile, Packages, PkgDeps1, Graph, Upgrade, Seen, State1), + {Solved, State4} = + case PkgDeps1 of + [] -> + {[], State1}; + _ -> + %% Read in package index and dep graph + {Packages, Graph} = rebar_state:packages(State1), + Registry = rebar_packages:registry(State1), + State2 = rebar_state:packages(rebar_state:registry(State1, Registry) + ,{Packages, Graph}), + + update_pkg_deps(Profile, Packages, PkgDeps1 + ,Graph, Upgrade, Seen, State2) + end, AllDeps = lists:ukeymerge(2 ,lists:ukeysort(2, SrcApps) ,lists:ukeysort(2, Solved)), - State3 = rebar_state:update_all_deps(State2, AllDeps), + State5 = rebar_state:update_all_deps(State4, AllDeps), CodePaths = [rebar_app_info:ebin_dir(A) || A <- AllDeps], - State4 = rebar_state:update_code_paths(State3, all_deps, CodePaths), + State6 = rebar_state:update_code_paths(State5, all_deps, CodePaths), - {ok, AllDeps, State4}. + {ok, AllDeps, State6}. %% =================================================================== %% Internal functions @@ -201,7 +212,7 @@ update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State) -> {Solved, State1}. handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, State) -> - AppInfo = package_to_app(DepsDir, Packages, Pkg), + AppInfo = package_to_app(DepsDir, Packages, Pkg, State), Level = rebar_app_info:dep_level(AppInfo), {NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level), {_, AppInfo1} = maybe_fetch(AppInfo, Profile, Upgrade, Seen, NewState), @@ -233,10 +244,15 @@ maybe_lock(Profile, AppInfo, Seen, State, Level) -> {Seen, State} end. -package_to_app(DepsDir, Packages, {Name, Vsn, Level}) -> +package_to_app(DepsDir, Packages, {Name, Vsn, Level}, State) -> case dict:find({Name, Vsn}, Packages) of error -> - throw(?PRV_ERROR({missing_package, Name, Vsn})); + case rebar_packages:check_registry(Name, Vsn, State) of + true -> + throw(?PRV_ERROR({not_rebar_package, Name, Vsn})); + false -> + throw(?PRV_ERROR({missing_package, Name, Vsn})) + end; {ok, P} -> PkgDeps = [{PkgName, PkgVsn} || {PkgName,PkgVsn} <- proplists:get_value(<<"deps">>, P, [])], @@ -617,7 +633,7 @@ not_needs_compile(App) -> get_package(Dep, State) -> case rebar_state:registry(State) of {ok, T} -> - HighestDepVsn = rebar_packages:find_highest_matching(Dep, "0", T), + {ok, HighestDepVsn} = rebar_packages:find_highest_matching(Dep, "0", T), {Dep, HighestDepVsn}; error -> throw(?PRV_ERROR({load_registry_fail, Dep})) diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 6153952..ec2f692 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -52,7 +52,7 @@ init(State) -> {example, "rebar3 shell"}, {short_desc, "Run shell with project apps and deps in path."}, {desc, info()}, - {opts, []}])), + {opts, [{config, undefined, "config", string, "Path to the config file to use. Defaults to the sys_config defined for relx, if present."}]}])), {ok, State1}. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. @@ -96,6 +96,8 @@ shell(State) -> code:add_pathsa(rebar_state:code_paths(State, all_deps)), %% add project app test paths ok = add_test_paths(State), + %% try to read in sys.config file + ok = reread_config(State), %% this call never returns (until user quits shell) timer:sleep(infinity). @@ -129,3 +131,61 @@ add_test_paths(State) -> end, rebar_state:project_apps(State)), _ = code:add_path(filename:join([rebar_dir:base_dir(State), "test"])), ok. + +reread_config(State) -> + case find_config(State) of + no_config -> + ok; + {ok, ConfigList} -> + lists:foreach(fun ({Application, Items}) -> + lists:foreach(fun ({Key, Val}) -> + application:set_env(Application, Key, Val) + end, + Items) + end, + ConfigList); + {error, Error} -> + ?ABORT("Error while attempting to read configuration file: ~p", [Error]) + end. + +% First try the --config flag, then try the relx sys_config +-spec find_config(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}. +find_config(State) -> + case find_config_option(State) of + no_config -> + find_config_relx(State); + Result -> + Result + end. + +-spec find_config_option(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}. +find_config_option(State) -> + {Opts, _} = rebar_state:command_parsed_args(State), + case proplists:get_value(config, Opts) of + undefined -> + no_config; + Filename -> + consult_config(State, Filename) + end. + +-spec find_config_relx(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}. +find_config_relx(State) -> + case proplists:get_value(sys_config, rebar_state:get(State, relx, [])) of + undefined -> + no_config; + Filename -> + consult_config(State, Filename) + end. + +-spec consult_config(rebar_state:t(), string()) -> {ok, [tuple()]}|{error, tuple()}. +consult_config(State, Filename) -> + Fullpath = filename:join(rebar_dir:root_dir(State), Filename), + ?DEBUG("Loading configuration from ~p", [Fullpath]), + case file:consult(Fullpath) of + {ok, [Config]} -> + {ok, Config}; + {ok, []} -> + {ok, []}; + {error, Error} -> + {error, {Error, Fullpath}} + end. diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index d0ff889..942b386 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -52,7 +52,8 @@ do(State) -> write_registry(Dict, Graph, State), ok catch - _E:_C -> + _E:C -> + ?DEBUG("Error creating package index: ~p ~p", [C, erlang:get_stacktrace()]), throw(?PRV_ERROR(package_index_write)) end, @@ -71,6 +72,10 @@ write_registry(Dict, {digraph, Edges, Vertices, Neighbors, _}, State) -> ets:tab2file(Neighbors, filename:join(RegistryDir, "neighbors")), file:write_file(filename:join(RegistryDir, "dict"), term_to_binary(Dict)). +is_supported(<<"make">>) -> true; +is_supported(<<"rebar">>) -> true; +is_supported(_) -> false. + hex_to_graph(Filename) -> {ok, T} = ets:file2tab(Filename), Graph = digraph:new(), @@ -82,9 +87,14 @@ hex_to_graph(Filename) -> ok end, ok, T), - Dict1 = ets:foldl(fun({{Pkg, PkgVsn}, [Deps | _]}, Dict) -> - DepsList = update_graph(Pkg, PkgVsn, Deps, T, Graph), - dict:store({Pkg, PkgVsn}, DepsList, Dict); + Dict1 = ets:foldl(fun({{Pkg, PkgVsn}, [Deps, _, BuildTools | _]}, Dict) when is_list(BuildTools) -> + case lists:any(fun is_supported/1, BuildTools) of + true -> + DepsList = update_graph(Pkg, PkgVsn, Deps, T, Graph), + dict:store({Pkg, PkgVsn}, DepsList, Dict); + false -> + Dict + end; (_, Dict) -> Dict end, dict:new(), T), @@ -94,9 +104,13 @@ update_graph(Pkg, PkgVsn, Deps, HexRegistry, Graph) -> lists:foldl(fun([Dep, DepVsn, false, _AppName | _], DepsListAcc) -> case DepVsn of <<"~> ", Vsn/binary>> -> - HighestDepVsn = rebar_packages:find_highest_matching(Dep, Vsn, HexRegistry), - digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, HighestDepVsn}), - [{Dep, DepVsn} | DepsListAcc]; + case rebar_packages:find_highest_matching(Dep, Vsn, HexRegistry) of + {ok, HighestDepVsn} -> + digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, HighestDepVsn}), + [{Dep, DepVsn} | DepsListAcc]; + none -> + DepsListAcc + end; Vsn -> digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, Vsn}), [{Dep, Vsn} | DepsListAcc] diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl index d7d2921..05845e4 100644 --- a/src/rebar_prv_upgrade.erl +++ b/src/rebar_prv_upgrade.erl @@ -140,4 +140,3 @@ info_useless(Old, New) -> || Name <- Old, not lists:member(Name, New)], ok. - diff --git a/src/rebar_state.erl b/src/rebar_state.erl index ddac9d2..7a6e60d 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -69,25 +69,28 @@ -spec new() -> t(). new() -> - #state_t{dir = rebar_dir:get_cwd()}. + BaseState = base_state(), + BaseState#state_t{dir = rebar_dir:get_cwd()}. -spec new(list()) -> t(). new(Config) when is_list(Config) -> + BaseState = base_state(), Deps = proplists:get_value(deps, Config, []), Opts = dict:from_list([{{deps, default}, Deps} | Config]), - #state_t { dir = rebar_dir:get_cwd(), - default = Opts, - opts = Opts }. + BaseState#state_t { dir = rebar_dir:get_cwd(), + default = Opts, + opts = Opts }. -spec new(t() | atom(), list()) -> t(). new(Profile, Config) when is_atom(Profile) , is_list(Config) -> + BaseState = base_state(), Deps = proplists:get_value(deps, Config, []), Opts = dict:from_list([{{deps, default}, Deps} | Config]), - #state_t { dir = rebar_dir:get_cwd(), - current_profiles = [Profile], - default = Opts, - opts = Opts }; + BaseState#state_t { dir = rebar_dir:get_cwd(), + current_profiles = [Profile], + default = Opts, + opts = Opts }; new(ParentState=#state_t{}, Config) -> %% Load terms from rebar.config, if it exists Dir = rebar_dir:get_cwd(), @@ -113,6 +116,15 @@ new(ParentState, Config, Dir) -> ,opts=NewOpts ,default=NewOpts}. +base_state() -> + case application:get_env(rebar, resources) of + undefined -> + Resources = []; + {ok, Resources} -> + Resources + end, + #state_t{resources=Resources}. + get(State, Key) -> {ok, Value} = dict:find(Key, State#state_t.opts), Value. @@ -229,16 +241,28 @@ apply_profiles(State, Profile) when not is_list(Profile) -> apply_profiles(State, [Profile]); apply_profiles(State, [default]) -> State; -apply_profiles(State=#state_t{opts=Opts, current_profiles=CurrentProfiles}, Profiles) -> +apply_profiles(State=#state_t{default = Defaults, current_profiles=CurrentProfiles}, Profiles) -> + AppliedProfiles = deduplicate(CurrentProfiles ++ Profiles), ConfigProfiles = rebar_state:get(State, profiles, []), - {Profiles1, NewOpts} = - lists:foldl(fun(default, {ProfilesAcc, OptsAcc}) -> - {ProfilesAcc, OptsAcc}; - (Profile, {ProfilesAcc, OptsAcc}) -> + NewOpts = + lists:foldl(fun(default, OptsAcc) -> + OptsAcc; + (Profile, OptsAcc) -> ProfileOpts = dict:from_list(proplists:get_value(Profile, ConfigProfiles, [])), - {[Profile]++ProfilesAcc, merge_opts(Profile, ProfileOpts, OptsAcc)} - end, {[], Opts}, Profiles), - State#state_t{current_profiles=CurrentProfiles++Profiles1, opts=NewOpts}. + merge_opts(Profile, ProfileOpts, OptsAcc) + end, Defaults, AppliedProfiles), + State#state_t{current_profiles = AppliedProfiles, opts=NewOpts}. + +deduplicate(Profiles) -> + do_deduplicate(lists:reverse(Profiles), []). + +do_deduplicate([], Acc) -> + Acc; +do_deduplicate([Head | Rest], Acc) -> + case lists:member(Head, Acc) of + true -> do_deduplicate(Rest, Acc); + false -> do_deduplicate(Rest, [Head | Acc]) + end. merge_opts(Profile, NewOpts, OldOpts) -> Opts = merge_opts(NewOpts, OldOpts), diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 746e440..353fa36 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -29,14 +29,10 @@ -export([new/4, list_templates/1]). -%% API for other utilities that need templating functionality --export([resolve_variables/2, - render/2]). -include("rebar.hrl"). -define(TEMPLATE_RE, "^[^._].*\\.template\$"). --define(ERLYDTL_COMPILE_OPTS, [report_warnings, return_errors, {auto_escape, false}, {out_dir, false}]). %% =================================================================== %% Public API @@ -45,7 +41,7 @@ %% Apply a template new(Template, Vars, Force, State) -> {AvailTemplates, Files} = find_templates(State), - ?DEBUG("Looking for ~p~n", [Template]), + ?DEBUG("Looking for ~p", [Template]), case lists:keyfind(Template, 1, AvailTemplates) of false -> {not_found, Template}; TemplateTup -> create(TemplateTup, Files, Vars, Force, State) @@ -57,34 +53,6 @@ list_templates(State) -> [list_template(Files, Template, State) || Template <- AvailTemplates]. %% =================================================================== -%% Rendering API / legacy? -%% =================================================================== - -%% Given a list of key value pairs, for each string value attempt to -%% render it using Dict as the context. Storing the result in Dict as Key. -%% -resolve_variables([], Dict) -> - Dict; -resolve_variables([{Key, Value0} | Rest], Dict) when is_list(Value0) -> - Value = render(Value0, Dict), - resolve_variables(Rest, dict:store(Key, Value, Dict)); -resolve_variables([{Key, {list, Dicts}} | Rest], Dict) when is_list(Dicts) -> - %% just un-tag it so erlydtl can use it - resolve_variables(Rest, dict:store(Key, Dicts, Dict)); -resolve_variables([_Pair | Rest], Dict) -> - resolve_variables(Rest, Dict). - -%% -%% Render a binary to a string, using erlydtl and the specified context -%% -render(Template, Context) when is_atom(Template) -> - Template:render(Context); -render(Template, Context) -> - Module = list_to_atom(Template++"_dtl"), - Module:render(Context). - - -%% =================================================================== %% Internal Functions %% =================================================================== @@ -191,7 +159,7 @@ create({Template, Type, File}, Files, UserVars, Force, State) -> %% Run template instructions one at a time. execute_template([], _, {Template,_,_}, _, _) -> - ?DEBUG("Template ~s applied~n", [Template]), + ?DEBUG("Template ~s applied", [Template]), ok; %% We can't execute the description execute_template([{description, _} | Terms], Files, Template, Vars, Force) -> @@ -201,13 +169,13 @@ execute_template([{variables, _} | Terms], Files, Template, Vars, Force) -> execute_template(Terms, Files, Template, Vars, Force); %% Create a directory execute_template([{dir, Path} | Terms], Files, Template, Vars, Force) -> - ?DEBUG("Creating directory ~p~n", [Path]), + ?DEBUG("Creating directory ~p", [Path]), case ec_file:mkdir_p(expand_path(Path, Vars)) of ok -> ok; {error, Reason} -> ?ABORT("Failed while processing template instruction " - "{dir, ~p}: ~p~n", [Path, Reason]) + "{dir, ~p}: ~p", [Path, Reason]) end, execute_template(Terms, Files, Template, Vars, Force); %% Change permissions on a file @@ -218,29 +186,28 @@ execute_template([{chmod, File, Perm} | Terms], Files, Template, Vars, Force) -> execute_template(Terms, Files, Template, Vars, Force); {error, Reason} -> ?ABORT("Failed while processing template instruction " - "{chmod, ~.8#, ~p}: ~p~n", [Perm, File, Reason]) + "{chmod, ~.8#, ~p}: ~p", [Perm, File, Reason]) end; %% Create a raw untemplated file execute_template([{file, From, To} | Terms], Files, {Template, Type, Cwd}, Vars, Force) -> - ?DEBUG("Creating file ~p~n", [To]), + ?DEBUG("Creating file ~p", [To]), Data = load_file(Files, Type, filename:join(Cwd, From)), Out = expand_path(To,Vars), case write_file(Out, Data, Force) of ok -> ok; - {error, exists} -> ?INFO("File ~p already exists.~n", [Out]) + {error, exists} -> ?INFO("File ~p already exists.", [Out]) end, execute_template(Terms, Files, {Template, Type, Cwd}, Vars, Force); %% Operate on a django template execute_template([{template, From, To} | Terms], Files, {Template, Type, Cwd}, Vars, Force) -> - ?DEBUG("Executing template file ~p~n", [From]), + ?DEBUG("Executing template file ~p", [From]), Out = expand_path(To, Vars), Tpl = load_file(Files, Type, filename:join(Cwd, From)), - TplName = make_template_name("rebar_template", Out), - {ok, Mod} = erlydtl:compile_template(Tpl, TplName, ?ERLYDTL_COMPILE_OPTS), - {ok, Output} = Mod:render(Vars), - case write_file(Out, Output, Force) of - ok -> ok; - {error, exists} -> ?INFO("File ~p already exists~n", [Out]) + case write_file(Out, render(Tpl, Vars), Force) of + ok -> + ok; + {error, exists} -> + ?INFO("File ~p already exists", [Out]) end, execute_template(Terms, Files, {Template, Type, Cwd}, Vars, Force); %% Unknown @@ -342,11 +309,11 @@ prioritize_templates([{Name, Type, File} | Rest], Valid) -> prioritize_templates(Rest, [{Name, Type, File} | Valid]); {_, escript, _} -> ?DEBUG("Skipping template ~p, due to presence of a built-in " - "template with the same name~n", [Name]), + "template with the same name", [Name]), prioritize_templates(Rest, Valid); {_, file, _} -> ?DEBUG("Skipping template ~p, due to presence of a custom " - "template at ~s~n", [Name, File]), + "template at ~s", [Name, File]), prioritize_templates(Rest, Valid) end. @@ -409,12 +376,8 @@ write_file(Output, Data, Force) -> {error, exists} end. --spec make_template_name(string(), term()) -> module(). -make_template_name(Base, Value) -> - %% Seed so we get different values each time - random:seed(os:timestamp()), - Hash = erlang:phash2(Value), - Ran = random:uniform(10000000), - erlang:list_to_atom(Base ++ "_" ++ - erlang:integer_to_list(Hash) ++ - "_" ++ erlang:integer_to_list(Ran)). +%% +%% Render a binary to a string, using mustache and the specified context +%% +render(Bin, Context) -> + mustache:render(ec_cnv:to_binary(Bin), Context, [{key_type, atom}]). diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 49f7ad7..160d547 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -42,7 +42,7 @@ erl_to_mod/1, beams/1, find_executable/1, - vcs_vsn/2, + vcs_vsn/3, deprecated/3, deprecated/4, erl_opts/1, @@ -53,7 +53,8 @@ get_arch/0, wordsize/0, tup_umerge/2, - tup_sort/1]). + tup_sort/1, + line_count/1]). %% for internal use only -export([otp_release/0]). @@ -281,6 +282,11 @@ umerge([], Olds, Merged, CmpMerged, Cmp) when CmpMerged == Cmp -> umerge([], Olds, Merged, _CmpMerged, Cmp) -> lists:reverse(Olds, [Cmp | Merged]). +%% Implements wc -l functionality used to determine patchcount from git output +line_count(PatchLines) -> + Tokenized = string:tokens(PatchLines, "\n"), + {ok, length(Tokenized)}. + %% ==================================================================== %% Internal functions %% ==================================================================== @@ -298,27 +304,30 @@ otp_release1([$R,N|_]=Rel) when is_integer(N) -> %% the "\n". otp_release1(Rel) -> File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]), - {ok, Vsn} = file:read_file(File), - - %% It's fine to rely on the binary module here because we can - %% be sure that it's available when the otp_release string does - %% not begin with $R. - Size = byte_size(Vsn), - %% The shortest vsn string consists of at least two digits - %% followed by "\n". Therefore, it's safe to assume Size >= 3. - case binary:part(Vsn, {Size, -3}) of - <<"**\n">> -> - %% The OTP documentation mentions that a system patched - %% using the otp_patch_apply tool available to licensed - %% customers will leave a '**' suffix in the version as a - %% flag saying the system consists of application versions - %% from multiple OTP versions. We ignore this flag and - %% drop the suffix, given for all intents and purposes, we - %% cannot obtain relevant information from it as far as - %% tooling is concerned. - binary:bin_to_list(Vsn, {0, Size - 3}); - _ -> - binary:bin_to_list(Vsn, {0, Size - 1}) + case file:read_file(File) of + {error, _} -> + Rel; + {ok, Vsn} -> + %% It's fine to rely on the binary module here because we can + %% be sure that it's available when the otp_release string does + %% not begin with $R. + Size = byte_size(Vsn), + %% The shortest vsn string consists of at least two digits + %% followed by "\n". Therefore, it's safe to assume Size >= 3. + case binary:part(Vsn, {Size, -3}) of + <<"**\n">> -> + %% The OTP documentation mentions that a system patched + %% using the otp_patch_apply tool available to licensed + %% customers will leave a '**' suffix in the version as a + %% flag saying the system consists of application versions + %% from multiple OTP versions. We ignore this flag and + %% drop the suffix, given for all intents and purposes, we + %% cannot obtain relevant information from it as far as + %% tooling is concerned. + binary:bin_to_list(Vsn, {0, Size - 3}); + _ -> + binary:bin_to_list(Vsn, {0, Size - 1}) + end end. %% We do the shell variable substitution ourselves on Windows and hope that the @@ -471,8 +480,8 @@ escript_foldl(Fun, Acc, File) -> Error end. -vcs_vsn(Vcs, Dir) -> - case vcs_vsn_cmd(Vcs, Dir) of +vcs_vsn(Vcs, Dir, Resources) -> + case vcs_vsn_cmd(Vcs, Dir, Resources) of {plain, VsnString} -> VsnString; {cmd, CmdString} -> @@ -484,23 +493,48 @@ vcs_vsn(Vcs, Dir) -> end. %% Temp work around for repos like relx that use "semver" -vcs_vsn_cmd(VCS, Dir) when VCS =:= semver ; VCS =:= "semver" -> - rebar_git_resource:make_vsn(Dir); -vcs_vsn_cmd(VCS, Dir) when VCS =:= git ; VCS =:= "git" -> - rebar_git_resource:make_vsn(Dir); -vcs_vsn_cmd(VCS, Dir) when VCS =:= pkg ; VCS =:= "pkg" -> - rebar_pkg_resource:make_vsn(Dir); -vcs_vsn_cmd({cmd, _Cmd}=Custom, _) -> +vcs_vsn_cmd(VCS, Dir, Resources) when VCS =:= semver ; VCS =:= "semver" -> + vcs_vsn_cmd(git, Dir, Resources); +vcs_vsn_cmd({cmd, _Cmd}=Custom, _, _) -> Custom; -vcs_vsn_cmd(Version, _) when is_list(Version) -> - {plain, Version}; -vcs_vsn_cmd(_, _) -> +vcs_vsn_cmd(VCS, Dir, Resources) when is_atom(VCS) -> + case find_resource_module(VCS, Resources) of + {ok, Module} -> + Module:make_vsn(Dir); + {error, _} -> + unknown + end; +vcs_vsn_cmd(VCS, Dir, Resources) when is_list(VCS) -> + try list_to_existing_atom(VCS) of + AVCS -> + case vcs_vsn_cmd(AVCS, Dir, Resources) of + unknown -> {plain, VCS}; + Other -> Other + end + catch + error:badarg -> + {plain, VCS} + end; +vcs_vsn_cmd(_, _, _) -> unknown. vcs_vsn_invoke(Cmd, Dir) -> {ok, VsnString} = rebar_utils:sh(Cmd, [{cd, Dir}, {use_stdout, false}]), string:strip(VsnString, right, $\n). +find_resource_module(Type, Resources) -> + case lists:keyfind(Type, 1, Resources) of + false -> + case code:which(Type) of + non_existing -> + {error, unknown}; + _ -> + {ok, Type} + end; + {Type, Module} -> + {ok, Module} + end. + %% %% Filter a list of erl_opts platform_define options such that only %% those which match the provided architecture regex are returned. diff --git a/test/mock_pkg_resource.erl b/test/mock_pkg_resource.erl index 615e8a5..560caef 100644 --- a/test/mock_pkg_resource.erl +++ b/test/mock_pkg_resource.erl @@ -152,7 +152,7 @@ find_parts([{AppName, Deps}|Rest], Skip, Acc) -> end. to_graph_parts(Dict) -> - LastUpdated = now(), + LastUpdated = os:timestamp(), dict:fold(fun(K,V,{Ks,Vs}) -> {_,Deps} = lists:keyfind(<<"deps">>, 1, V), {[{K,LastUpdated}|Ks], diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index ee262a1..bdab075 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -19,7 +19,8 @@ delete_beam_if_source_deleted/1, checkout_priority/1, compile_plugins/1, - highest_version_of_pkg_dep/1]). + highest_version_of_pkg_dep/1, + parse_transform_test/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -46,7 +47,8 @@ all() -> build_all_srcdirs, recompile_when_hrl_changes, recompile_when_opts_change, dont_recompile_when_opts_dont_change, dont_recompile_yrl_or_xrl, delete_beam_if_source_deleted, - deps_in_path, checkout_priority, compile_plugins, highest_version_of_pkg_dep]. + deps_in_path, checkout_priority, compile_plugins, highest_version_of_pkg_dep, + parse_transform_test]. build_basic_app(Config) -> AppDir = ?config(apps, Config), @@ -449,3 +451,24 @@ highest_version_of_pkg_dep(Config) -> Config, RConf, ["compile"], {ok, [{app, Name}, {dep, PkgName, <<"0.1.3">>}]} ). + +parse_transform_test(Config) -> + AppDir = ?config(apps, Config), + + RebarConfig = [{erl_opts, [{parse_transform, pascal}]}], + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + ExtraSrc = <<"-module(pascal). " + "-export([parse_transform/2]). " + "parse_transform(Forms, _Options) -> " + "Forms.">>, + + ok = file:write_file(filename:join([AppDir, "src", "pascal.erl"]), ExtraSrc), + + rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}), + + EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]), + true = filelib:is_file(filename:join([EbinDir, "pascal.beam"])). diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl index 3c4aed3..0a86127 100644 --- a/test/rebar_ct_SUITE.erl +++ b/test/rebar_ct_SUITE.erl @@ -16,7 +16,8 @@ single_unmanaged_suite/1, multi_suite/1, all_suite/1, - single_dir_and_single_suite/1]). + single_dir_and_single_suite/1, + symlinked_dir_overwritten_fix/1]). -include_lib("common_test/include/ct.hrl"). @@ -36,7 +37,8 @@ groups() -> [{basic_app, [], [basic_app_default_dirs, single_unmanaged_suite, multi_suite, all_suite, - single_dir_and_single_suite]}]. + single_dir_and_single_suite, + symlinked_dir_overwritten_fix]}]. init_per_group(basic_app, Config) -> C = rebar_test_utils:init_rebar_state(Config, "ct_"), @@ -513,6 +515,36 @@ single_dir_and_single_suite(Config) -> Expect = Suite. +symlinked_dir_overwritten_fix(Config) -> + AppDir = ?config(apps, Config), + [Name1, _Name2] = ?config(appnames, Config), + + {ok, State} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return), + + LibDirs = rebar_dir:lib_dirs(State), + State1 = rebar_app_discover:do(State, LibDirs), + + Providers = rebar_state:providers(State1), + Namespace = rebar_state:namespace(State1), + CommandProvider = providers:get_provider(ct, Providers, Namespace), + GetOptSpec = providers:opts(CommandProvider), + {ok, GetOptResult} = getopt:parse(GetOptSpec, + ["--dir=" ++ filename:join([AppDir, + "apps", + Name1])]), + + State2 = rebar_state:command_parsed_args(State1, GetOptResult), + + Result = rebar_prv_common_test:setup_ct(State2), + + Expect = filename:absname(filename:join([AppDir, "_build", "test", "lib", Name1])), + Dir = proplists:get_value(dir, Result), + + Expect = Dir, + + {ok, _} = rebar_test_utils:run_and_check(Config, [], ["as", "test", "compile"], return). + + test_suite(Name) -> io_lib:format("-module(~ts_SUITE).\n" "-compile(export_all).\n" diff --git a/test/rebar_erlydtl_SUITE.erl b/test/rebar_erlydtl_SUITE.erl deleted file mode 100644 index c9054fd..0000000 --- a/test/rebar_erlydtl_SUITE.erl +++ /dev/null @@ -1,72 +0,0 @@ -%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- -%% ex: ts=4 sw=4 et --module(rebar_erlydtl_SUITE). - --export([suite/0, - init_per_suite/1, - end_per_suite/1, - init_per_testcase/2, - end_per_testcase/2, - all/0, - compile/1]). - --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("kernel/include/file.hrl"). - -%% =================================================================== -%% common_test callbacks -%% =================================================================== - -suite() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - - -init_per_testcase(_, Config) -> - UpdConfig = rebar_test_utils:init_rebar_state(Config), - AppDir = ?config(apps, UpdConfig), - - Name = rebar_test_utils:create_random_name("erlydtlapp_"), - Vsn = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), - - write_dtl_file(AppDir, Name), - - RebarConfig = [{erl_opts, [debug_info]}, - {erlydtl_opts, []}], - [{app_name, Name}, - {rebar_config, RebarConfig} | UpdConfig]. - -end_per_testcase(_, _Config) -> - ok. - -all() -> - [compile]. - -compile(Config) -> - AppDir = ?config(apps, Config), - AppName = ?config(app_name, Config), - RebarConfig = ?config(rebar_config, Config), - Beam = beam_file(AppDir, AppName), - rebar_test_utils:run_and_check( - Config, RebarConfig, ["erlydtl", "compile"], - {ok, [{file, Beam}]} - ). - -beam_file(AppDir, AppName) -> - filename:join([AppDir, "_build", "default", "lib", - AppName, "ebin", AppName++"_template_dtl.beam"]). - -write_dtl_file(Dir, AppName) -> - Erl = filename:join([Dir, "priv", "templates", AppName++"_template.dtl"]), - ok = filelib:ensure_dir(Erl), - ok = ec_file:write(Erl, get_body()). - -get_body() -> - ["[]"]. diff --git a/test/rebar_profiles_SUITE.erl b/test/rebar_profiles_SUITE.erl index 2d03432..b42df39 100644 --- a/test/rebar_profiles_SUITE.erl +++ b/test/rebar_profiles_SUITE.erl @@ -7,11 +7,16 @@ all/0, profile_new_key/1, profile_merge_keys/1, + explicit_profile_deduplicate_deps/1, + implicit_profile_deduplicate_deps/1, all_deps_code_paths/1, profile_merges/1, + same_profile_deduplication/1, + stack_deduplication/1, add_to_profile/1, add_to_existing_profile/1, profiles_remain_applied_with_config_present/1, + deduplicated_paths/1, test_profile_applied_at_completion/1, test_profile_applied_before_compile/1, test_profile_applied_before_eunit/1, @@ -23,8 +28,11 @@ all() -> [profile_new_key, profile_merge_keys, all_deps_code_paths, profile_merges, + explicit_profile_deduplicate_deps, implicit_profile_deduplicate_deps, + same_profile_deduplication, stack_deduplication, add_to_profile, add_to_existing_profile, profiles_remain_applied_with_config_present, + deduplicated_paths, test_profile_applied_at_completion, test_profile_applied_before_compile, test_profile_applied_before_eunit, @@ -96,6 +104,66 @@ profile_merge_keys(Config) -> ,{dep, "a", "1.0.0"} ,{dep, "b", "2.0.0"}]}). +explicit_profile_deduplicate_deps(Config) -> + AppDir = ?config(apps, Config), + + AllDeps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []} + ,{"a", "2.0.0", []} + ,{"b", "1.0.0", []} + ,{"b", "2.0.0", []}]), + mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]), + + Name = rebar_test_utils:create_random_name("explicit_profile_deduplicate_deps_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + FooDeps = rebar_test_utils:top_level_deps( + rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}, + {"b", "2.0.0", []}])), + BarDeps = rebar_test_utils:top_level_deps( + rebar_test_utils:expand_deps(git, [{"b", "1.0.0", []}])), + + RebarConfig = [{profiles, + [{foo, + [{deps, FooDeps}]}, + {bar, + [{deps, BarDeps}]}]}], + + rebar_test_utils:run_and_check(Config, RebarConfig, + ["as", "bar,foo,bar", "compile"], {ok, [{app, Name} + ,{dep, "a", "1.0.0"} + ,{dep, "b", "1.0.0"}]}). + +implicit_profile_deduplicate_deps(Config) -> + AppDir = ?config(apps, Config), + + AllDeps = rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []} + ,{"a", "2.0.0", []} + ,{"b", "1.0.0", []} + ,{"b", "2.0.0", []}]), + mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(AllDeps)}]), + + Name = rebar_test_utils:create_random_name("implicit_profile_deduplicate_deps_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + TestDeps = rebar_test_utils:top_level_deps( + rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}, + {"b", "2.0.0", []}])), + ProfileDeps = rebar_test_utils:top_level_deps( + rebar_test_utils:expand_deps(git, [{"b", "1.0.0", []}])), + + RebarConfig = [{profiles, + [{test, + [{deps, TestDeps}]}, + {bar, + [{deps, ProfileDeps}]}]}], + + rebar_test_utils:run_and_check(Config, RebarConfig, + ["as", "test,bar", "eunit"], {ok, [{app, Name} + ,{dep, "a", "1.0.0"} + ,{dep, "b", "2.0.0"}]}). + all_deps_code_paths(Config) -> AppDir = ?config(apps, Config), @@ -162,6 +230,72 @@ profile_merges(_Config) -> [{key5, false}, {key5, true}] = rebar_state:get(State1, test5), [{key6, true}, {key6, false}] = rebar_state:get(State1, test6). +same_profile_deduplication(_Config) -> + RebarConfig = [{test1, [{key1, 1, 2}, key2]}, + {test2, [foo]}, + {test3, [key3]}, + {profiles, + [{profile1, + [{test1, [{key3, 5}, {key2, "hello"}]}, + {test2, [bar]}, + {test3, []} + ]}] + }], + State = rebar_state:new(RebarConfig), + State1 = rebar_state:apply_profiles(State, [profile1, profile1, profile1]), + + ?assertEqual([default, profile1], rebar_state:current_profiles(State1)), + Test1 = rebar_state:get(State1, test1), + + %% Combine lists + ?assertEqual(lists:sort([key2, {key1, 1, 2}, {key3, 5}, {key2, "hello"}]), + lists:sort(Test1)), + + %% Key2 from profile1 overrides key2 from default profile + ?assertEqual("hello", proplists:get_value(key2, Test1)), + + %% Check that a newvalue of []/"" doesn't override non-string oldvalues + ?assertEqual([key3], rebar_state:get(State1, test3)), + ?assertEqual([bar, foo], rebar_state:get(State1, test2)). + +stack_deduplication(_Config) -> + RebarConfig = [ + {test_key, default}, + {test_list, [ {foo, default} ]}, + {profiles, [ + {a, [ + {test_key, a}, + {test_list, [ {foo, a} ]} + ]}, + {b, [ + {test_key, b}, + {test_list, [ {foo, b} ]} + ]}, + {c, [ + {test_key, c}, + {test_list, [ {foo, c} ]} + ]}, + {d, [ + {test_key, d}, + {test_list, [ {foo, d} ]} + ]}, + {e, [ + {test_key, e}, + {test_list, [ {foo, e} ]} + ]} + ]} + ], + State = rebar_state:new(RebarConfig), + State1 = rebar_state:apply_profiles(State, [a, b, c, d, e, a, e, b]), + ?assertEqual(b, rebar_state:get(State1, test_key)), + + TestList = rebar_state:get(State1, test_list), + ?assertEqual( + [{foo, b}, {foo, e}, {foo, a}, {foo, d}, {foo, c}, {foo, default} ], + TestList + ), + ?assertEqual(b, proplists:get_value(foo, TestList)). + add_to_profile(_Config) -> RebarConfig = [{foo, true}, {bar, false}], State = rebar_state:new(RebarConfig), @@ -205,6 +339,22 @@ profiles_remain_applied_with_config_present(Config) -> true = lists:member({d, not_ok}, proplists:get_value(options, Mod:module_info(compile), [])). +deduplicated_paths(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("deduplicated_paths_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + RebarConfig = [], + rebar_test_utils:create_config(AppDir, RebarConfig), + rebar_test_utils:run_and_check(Config, RebarConfig, + ["as", "a,b,c,d,e,a,e,b", "compile"], + {ok, [{app, Name}]}), + + Path = filename:join([AppDir, "_build", "c+d+a+e+b", "lib", Name, "ebin"]), + ?assert(filelib:is_dir(Path)). + test_profile_applied_at_completion(Config) -> AppDir = ?config(apps, Config), diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index f764146..2cdc58b 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -104,12 +104,12 @@ create_config(AppDir, Contents) -> %% @doc Util to create a random variation of a given name. create_random_name(Name) -> - random:seed(erlang:now()), + random:seed(os:timestamp()), Name ++ erlang:integer_to_list(random:uniform(1000000)). %% @doc Util to create a random variation of a given version. create_random_vsn() -> - random:seed(erlang:now()), + random:seed(os:timestamp()), lists:flatten([erlang:integer_to_list(random:uniform(100)), ".", erlang:integer_to_list(random:uniform(100)), ".", erlang:integer_to_list(random:uniform(100))]). |