summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/rebar_app_utils.erl1
-rw-r--r--src/rebar_compiler.erl67
-rw-r--r--src/rebar_compiler_erl.erl12
-rw-r--r--src/rebar_file_utils.erl15
-rw-r--r--src/rebar_packages.erl6
-rw-r--r--src/rebar_prv_deps.erl185
-rw-r--r--src/rebar_prv_dialyzer.erl51
-rw-r--r--src/rebar_prv_escriptize.erl22
-rw-r--r--src/rebar_prv_eunit.erl30
-rw-r--r--src/rebar_prv_shell.erl76
-rw-r--r--src/rebar_string.erl29
-rw-r--r--src/rebar_templater.erl29
12 files changed, 372 insertions, 151 deletions
diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl
index bb706fd..91c095c 100644
--- a/src/rebar_app_utils.erl
+++ b/src/rebar_app_utils.erl
@@ -34,6 +34,7 @@
validate_application_info/2,
parse_deps/5,
parse_deps/6,
+ parse_dep/6,
expand_deps_sources/2,
dep_to_app/7,
format_error/1]).
diff --git a/src/rebar_compiler.erl b/src/rebar_compiler.erl
index 02c74db..55666ba 100644
--- a/src/rebar_compiler.erl
+++ b/src/rebar_compiler.erl
@@ -21,7 +21,10 @@
out_mappings => out_mappings()}.
-callback needed_files(digraph:graph(), [file:filename()], out_mappings(),
rebar_app_info:t()) ->
- {{[file:filename()], term()}, {[file:filename()], term()}}.
+ {{[file:filename()], term()}, % ErlFirstFiles (erl_opts global priority)
+ {[file:filename()] | % [Sequential]
+ {[file:filename()], [file:filename()]}, % {Sequential, Parallel}
+ term()}}.
-callback dependencies(file:filename(), file:dirname(), [file:dirname()]) -> [file:filename()].
-callback compile(file:filename(), out_mappings(), rebar_dict(), list()) ->
ok | {ok, [string()]} | {ok, [string()], [string()]}.
@@ -77,7 +80,13 @@ run(CompilerMod, AppInfo, Label) ->
true = digraph:delete(G),
compile_each(FirstFiles, FirstFileOpts, BaseOpts, Mappings, CompilerMod),
- compile_each(RestFiles, Opts, BaseOpts, Mappings, CompilerMod).
+ case RestFiles of
+ {Sequential, Parallel} -> % new parallelizable form
+ compile_each(Sequential, Opts, BaseOpts, Mappings, CompilerMod),
+ compile_parallel(Parallel, Opts, BaseOpts, Mappings, CompilerMod);
+ _ when is_list(RestFiles) -> % traditional sequential build
+ compile_each(RestFiles, Opts, BaseOpts, Mappings, CompilerMod)
+ end.
compile_each([], _Opts, _Config, _Outs, _CompilerMod) ->
ok;
@@ -99,6 +108,60 @@ compile_each([Source | Rest], Opts, Config, Outs, CompilerMod) ->
end,
compile_each(Rest, Opts, Config, Outs, CompilerMod).
+compile_worker(QueuePid, Opts, Config, Outs, CompilerMod) ->
+ QueuePid ! self(),
+ receive
+ {compile, Source} ->
+ Result = CompilerMod:compile(Source, Outs, Config, Opts),
+ QueuePid ! {Result, Source},
+ compile_worker(QueuePid, Opts, Config, Outs, CompilerMod);
+ empty ->
+ ok
+ end.
+
+compile_parallel([], _Opts, _BaseOpts, _Mappings, _CompilerMod) ->
+ ok;
+compile_parallel(Targets, Opts, BaseOpts, Mappings, CompilerMod) ->
+ Self = self(),
+ F = fun() -> compile_worker(Self, Opts, BaseOpts, Mappings, CompilerMod) end,
+ Jobs = min(length(Targets), erlang:system_info(schedulers)),
+ ?DEBUG("Starting ~B compile worker(s)", [Jobs]),
+ Pids = [spawn_monitor(F) || _I <- lists:seq(1, Jobs)],
+ compile_queue(Targets, Pids, Opts, BaseOpts, Mappings, CompilerMod).
+
+compile_queue([], [], _Opts, _Config, _Outs, _CompilerMod) ->
+ ok;
+compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod) ->
+ receive
+ Worker when is_pid(Worker), Targets =:= [] ->
+ Worker ! empty,
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ Worker when is_pid(Worker) ->
+ Worker ! {compile, hd(Targets)},
+ compile_queue(tl(Targets), Pids, Opts, Config, Outs, CompilerMod);
+ {ok, Source} ->
+ ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ {{ok, Warnings}, Source} ->
+ report(Warnings),
+ ?DEBUG("~sCompiled ~s", [rebar_utils:indent(1), filename:basename(Source)]),
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ {skipped, Source} ->
+ ?DEBUG("~sSkipped ~s", [rebar_utils:indent(1), filename:basename(Source)]),
+ compile_queue(Targets, Pids, Opts, Config, Outs, CompilerMod);
+ {Error, Source} ->
+ NewSource = format_error_source(Source, Config),
+ ?ERROR("Compiling ~ts failed", [NewSource]),
+ maybe_report(Error),
+ ?FAIL;
+ {'DOWN', Mref, _, Pid, normal} ->
+ Pids2 = lists:delete({Pid, Mref}, Pids),
+ compile_queue(Targets, Pids2, Opts, Config, Outs, CompilerMod);
+ {'DOWN', _Mref, _, _Pid, Info} ->
+ ?ERROR("Compilation failed: ~p", [Info]),
+ ?FAIL
+ end.
+
%% @doc remove compiled artifacts from an AppDir.
-spec clean([module()], rebar_app_info:t()) -> 'ok'.
clean(Compilers, AppInfo) ->
diff --git a/src/rebar_compiler_erl.erl b/src/rebar_compiler_erl.erl
index 759305c..1ad16d8 100644
--- a/src/rebar_compiler_erl.erl
+++ b/src/rebar_compiler_erl.erl
@@ -55,7 +55,14 @@ needed_files(Graph, FoundFiles, _, AppInfo) ->
{ErlFirstFiles, ErlOptsFirst} = erl_first_files(RebarOpts, ErlOpts, Dir, NeededErlFiles),
SubGraph = digraph_utils:subgraph(Graph, NeededErlFiles),
DepErlsOrdered = digraph_utils:topsort(SubGraph),
- OtherErls = lists:reverse(DepErlsOrdered),
+ %% Break out the files required by other modules from those
+ %% that none other depend of; the former must be sequentially
+ %% built, the rest is parallelizable.
+ OtherErls = lists:partition(
+ fun(Erl) -> digraph:in_degree(Graph, Erl) > 0 end,
+ lists:reverse([Dep || Dep <- DepErlsOrdered,
+ not lists:member(Dep, ErlFirstFiles)])
+ ),
PrivIncludes = [{i, filename:join(OutDir, Src)}
|| Src <- rebar_dir:all_src_dirs(RebarOpts, ["src"], [])],
@@ -64,8 +71,7 @@ needed_files(Graph, FoundFiles, _, AppInfo) ->
true = digraph:delete(SubGraph),
{{ErlFirstFiles, ErlOptsFirst ++ AdditionalOpts},
- {[Erl || Erl <- OtherErls,
- not lists:member(Erl, ErlFirstFiles)], ErlOpts ++ AdditionalOpts}}.
+ {OtherErls, ErlOpts ++ AdditionalOpts}}.
dependencies(Source, SourceDir, Dirs) ->
{ok, Fd} = file:open(Source, [read]),
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index 0e0dfe3..b2f34f0 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -28,6 +28,7 @@
-export([try_consult/1,
consult_config/2,
+ consult_config_terms/2,
format_error/1,
symlink_or_copy/2,
rm_rf/1,
@@ -66,6 +67,8 @@ try_consult(File) ->
throw(?PRV_ERROR({bad_term_file, File, Reason}))
end.
+%% @doc Parse a sys.config file and return the configuration terms
+%% for all its potentially nested configs.
-spec consult_config(rebar_state:t(), string()) -> [[tuple()]].
consult_config(State, Filename) ->
Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
@@ -74,6 +77,18 @@ consult_config(State, Filename) ->
[T] -> T;
[] -> []
end,
+ consult_config_terms(State, Config).
+
+%% @doc From a parsed sys.config file, expand all the terms to include
+%% its potential nested configs. It is also possible that no sub-terms
+%% (i.e. the config file does not refer to "some/other/file.config")
+%% that the input term is returned as-is.
+%%
+%% This function is added mostly to help with variable substitution
+%% and evaluation of 'sys.config.src' files, giving a way to handle
+%% expansion that is separate from regular config handling.
+-spec consult_config_terms(rebar_state:t(), [tuple()]) -> [[tuple()]].
+consult_config_terms(State, Config) ->
JoinedConfig = lists:flatmap(
fun (SubConfig) when is_list(SubConfig) ->
case lists:suffix(".config", SubConfig) of
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index a260e47..c7bbfac 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -238,7 +238,11 @@ update_package(Name, RepoConfig=#{name := Repo}, State) ->
_ = insert_releases(Name, Releases, Repo, ?PACKAGE_TABLE),
{ok, RegistryDir} = rebar_packages:registry_dir(State),
PackageIndex = filename:join(RegistryDir, ?INDEX_FILE),
- ok = ets:tab2file(?PACKAGE_TABLE, PackageIndex);
+ case ets:tab2file(?PACKAGE_TABLE, PackageIndex) of
+ ok -> ok;
+ {error, Error} ->
+ ?WARN("Failed to update package index at ~p: ~p", [PackageIndex, Error])
+ end;
{error, unverified} ->
?WARN(unverified_repo_message(), [Repo]),
fail;
diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl
index 577a859..d00a1c8 100644
--- a/src/rebar_prv_deps.erl
+++ b/src/rebar_prv_deps.erl
@@ -9,7 +9,7 @@
-include("rebar.hrl").
-define(PROVIDER, deps).
--define(DEPS, [app_discovery]).
+-define(DEPS, [install_deps]).
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
@@ -22,98 +22,127 @@ init(State) ->
{deps, ?DEPS},
{example, "rebar3 deps"},
{short_desc, "List dependencies"},
- {desc, "List dependencies. Those not matching lock files "
- "are followed by an asterisk (*)."},
+ {desc, "List dependencies. Those not matching "
+ "the config file are followed by "
+ "an asterisk (*)."},
{opts, []}])),
{ok, State1}.
+
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
Profiles = rebar_state:current_profiles(State),
- List = [{Profile, rebar_state:get(State, {deps, Profile}, [])}
- || Profile <- Profiles],
- [display(State, Profile, Deps) || {Profile, Deps} <- List],
+ [display(State, Profile) || Profile <- Profiles],
{ok, State}.
+
-spec format_error(any()) -> iolist().
format_error(Reason) ->
io_lib:format("~p", [Reason]).
-display(State, default, Deps) ->
- NewDeps = merge(Deps, rebar_state:get(State, deps, [])),
- display_deps(State, NewDeps),
+
+display(State, Profile = default) ->
+ display_profile_deps(State, Profile),
?CONSOLE("", []);
-display(State, Profile, Deps) ->
+display(State, Profile) ->
?CONSOLE("-- ~p --", [Profile]),
- display_deps(State, Deps),
+ display_profile_deps(State, Profile),
?CONSOLE("", []).
-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) ->
- atom_to_binary(Name, unicode);
-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: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)]
+
+display_profile_deps(State, Profile) ->
+ DepsDir = rebar_prv_install_deps:profile_dep_dir(State, Profile),
+
+ ProfileDeps = rebar_state:get(State, {deps, Profile}, []),
+ % ProfileDeps include those deps from rebar.lock that have been
+ % removed from rebar.config
+ ConfiguredDeps = [parse_dep_without_locks(DepsDir, Dep, State)
+ || Dep <- ProfileDeps],
+ LockedDepsMap = locked_deps_map(State, Profile),
+ [display_dep(State, Dep, LockedDepsMap) || Dep <- ConfiguredDeps].
+
+
+parse_dep_without_locks(DepsDir, Dep, State) ->
+ ParsedDep = rebar_app_utils:parse_dep(Dep, root, DepsDir, State, [], 0),
+ case Dep of
+ {_Name, Src, Level} when is_tuple(Src), is_integer(Level) ->
+ % This Dep is not in rebar.config but in rebar.lock
+ rebar_app_info:source(ParsedDep, undefined);
+ _ ->
+ rebar_app_utils:expand_deps_sources(ParsedDep, State)
+ end.
+
+
+locked_deps_map(State, Profile) ->
+ ParsedDeps = rebar_state:get(State, {parsed_deps, Profile}, []),
+ lists:foldl(fun(Dep, DepsIn) ->
+ case rebar_app_info:is_lock(Dep) of
+ true ->
+ DepName = rebar_app_info:name(Dep),
+ maps:put(rebar_utils:to_binary(DepName), Dep, DepsIn);
+ _ ->
+ DepsIn
+ end
+ end, maps:new(), ParsedDeps).
+
+
+display_dep(State, Dep, LockedDeps) ->
+ Name = rebar_utils:to_binary(rebar_app_info:name(Dep)),
+ NeedsUpdate = rebar_fetch:needs_update(Dep, State),
+ Source = rebar_app_info:source(Dep),
+ LockedSource = case maps:get(Name, LockedDeps, undefined) of
+ undefined -> undefined;
+ LockedDep -> rebar_app_info:source(LockedDep)
+ end,
+
+ display_dep_line(Name, NeedsUpdate, source_text(LockedSource), source_text(Source)).
+
+
+% Dep is a checkout
+display_dep_line(Name, _NeedsUpdate, _LockedSource, Source = checkout) ->
+ ?CONSOLE("~ts* (~ts)", [Name, Source]);
+
+% Dep exists only in lock file
+display_dep_line(Name, _NeedsUpdate, LockedSource, _Source = undefined) ->
+ ?CONSOLE("~ts* (locked ~ts <> none)", [Name, LockedSource]);
+
+% Dep not locked, report whether the disk copy matches the Source
+display_dep_line(Name, true, undefined, Source) ->
+ ?CONSOLE("~ts* (~ts)", [Name, Source]);
+display_dep_line(Name, _, undefined, Source) ->
+ ?CONSOLE("~ts (~ts)", [Name, Source]);
+
+% Dep locked, install_deps provider should have had updated the disk copy with
+% the locked version
+display_dep_line(Name, false, _LockedSource, Source) ->
+ % dep locked and no need to update (LockedSource and Source might not match
+ % because of one using {ref, X} and the other {tag, Y})
+ ?CONSOLE("~ts (locked ~ts)", [Name, Source]);
+display_dep_line(Name, _NeedsUpdate, LockedSource, Source) ->
+ % dep locked with mismatching lock and config files
+ ?CONSOLE("~ts* (locked ~ts <> ~ts)", [Name, LockedSource, Source]).
+
+
+source_text(Source) when is_list(Source); is_atom(Source) ->
+ Source;
+source_text({pkg, _Name, Vsn, _Hash, _RepoConfig}) ->
+ source_text({pkg, _Name, Vsn, _Hash});
+source_text({pkg, _Name, Vsn, _Hash}) ->
+ [<<"package">>, " ", rebar_utils:to_binary(Vsn)];
+source_text(Source) when is_tuple(Source), tuple_size(Source) < 3 ->
+ element(1, Source);
+source_text(Source) when is_tuple(Source) ->
+ Type = element(1, Source),
+ case element(3, Source) of
+ {ref , Ref} ->
+ SmallRef = case rebar_utils:to_binary(Ref) of
+ <<R:7/binary, _/binary>> -> <<R/binary, "...">>;
+ R -> R
+ end,
+ [atom_to_binary(Type, unicode), " source ", SmallRef];
+ {_ , Vsn} ->
+ [atom_to_binary(Type, unicode), " source ", rebar_utils:to_binary(Vsn)];
+ _ ->
+ [atom_to_binary(Type, unicode), " source"]
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("~ts* (package ~ts)", [rebar_utils:to_binary(Name), rebar_utils:to_binary(Vsn)]);
-display_dep(_State, Name) when is_binary(Name) ->
- ?CONSOLE("~ts* (package)", [Name]);
-display_dep(_State, {Name, Source}) when is_tuple(Source) ->
- ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]);
-display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) ->
- ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]);
-display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) ->
- ?CONSOLE("~ts* (~ts source)", [rebar_utils:to_binary(Name), type(Source)]);
-%% Locked
-display_dep(State, {Name, _Source={pkg, _, Vsn}, Level}) when is_integer(Level) ->
- DepsDir = rebar_dir:deps_dir(State),
- AppDir = filename:join([DepsDir, rebar_utils:to_binary(Name)]),
- {ok, AppInfo} = rebar_app_info:discover(AppDir),
- NeedsUpdate = case rebar_fetch:needs_update(AppInfo, State) of
- true -> "*";
- false -> ""
- end,
- ?CONSOLE("~ts~ts (locked package ~ts)", [Name, NeedsUpdate, Vsn]);
-display_dep(State, {Name, Source, Level}) when is_tuple(Source), is_integer(Level) ->
- DepsDir = rebar_dir:deps_dir(State),
- AppDir = filename:join([DepsDir, rebar_utils:to_binary(Name)]),
- {ok, AppInfo} = rebar_app_info:discover(AppDir),
- NeedsUpdate = case rebar_fetch:needs_update(AppInfo, State) of
- true -> "*";
- false -> ""
- end,
- ?CONSOLE("~ts~ts (locked ~ts source)", [Name, NeedsUpdate, type(Source)]).
-
-type(Source) when is_tuple(Source) -> element(1, Source).
diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl
index 585051c..82b4012 100644
--- a/src/rebar_prv_dialyzer.erl
+++ b/src/rebar_prv_dialyzer.erl
@@ -23,7 +23,11 @@
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Opts = [{update_plt, $u, "update-plt", boolean, "Enable updating the PLT. Default: true"},
- {succ_typings, $s, "succ-typings", boolean, "Enable success typing analysis. Default: true"}],
+ {succ_typings, $s, "succ-typings", boolean, "Enable success typing analysis. Default: true"},
+ {base_plt_location, undefined, "base-plt-location", string, "The location of base PLT file, defaults to $HOME/.cache/rebar3"},
+ {plt_location, undefined, "plt-location", string, "The location of the PLT file, defaults to the profile's base directory"},
+ {plt_prefix, undefined, "plt-prefix", string, "The prefix to the PLT file, defaults to \"rebar3\"" },
+ {base_plt_prefix, undefined, "base-plt-prefix", string, "The prefix to the base PLT file, defaults to \"rebar3\"" }],
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{bare, true},
@@ -87,10 +91,11 @@ do(State) ->
?INFO("Dialyzer starting, this may take a while...", []),
rebar_paths:unset_paths([plugins], State), % no plugins in analysis
rebar_paths:set_paths([deps], State),
- Plt = get_plt(State),
+ {Args, _} = rebar_state:command_parsed_args(State),
+ Plt = get_plt(Args, State),
try
- do(State, Plt)
+ do(Args, State, Plt)
catch
throw:{dialyzer_error, Error} ->
?PRV_ERROR({error_processing_apps, Error});
@@ -134,10 +139,10 @@ format_error(Reason) ->
%% Internal functions
-get_plt(State) ->
- Prefix = get_config(State, plt_prefix, ?PLT_PREFIX),
+get_plt(Args, State) ->
+ Prefix = proplists:get_value(plt_prefix, Args, get_config(State, plt_prefix, ?PLT_PREFIX)),
Name = plt_name(Prefix),
- case get_config(State, plt_location, local) of
+ case proplists:get_value(plt_location, Args, get_config(State, plt_location, local)) of
local ->
BaseDir = rebar_dir:base_dir(State),
filename:join(BaseDir, Name);
@@ -148,10 +153,10 @@ get_plt(State) ->
plt_name(Prefix) ->
Prefix ++ "_" ++ rebar_utils:otp_release() ++ "_plt".
-do(State, Plt) ->
+do(Args, State, Plt) ->
Output = get_output_file(State),
- {PltWarnings, State1} = update_proj_plt(State, Plt, Output),
- {Warnings, State2} = succ_typings(State1, Plt, Output),
+ {PltWarnings, State1} = update_proj_plt(Args, State, Plt, Output),
+ {Warnings, State2} = succ_typings(Args, State1, Plt, Output),
case PltWarnings + Warnings of
0 ->
{ok, State2};
@@ -174,23 +179,22 @@ get_output_file(State) ->
default_output_file() ->
rebar_utils:otp_release() ++ ".dialyzer_warnings".
-update_proj_plt(State, Plt, Output) ->
- {Args, _} = rebar_state:command_parsed_args(State),
+update_proj_plt(Args, State, Plt, Output) ->
case proplists:get_value(update_plt, Args) of
false ->
{0, State};
_ ->
- do_update_proj_plt(State, Plt, Output)
+ do_update_proj_plt(Args, State, Plt, Output)
end.
-do_update_proj_plt(State, Plt, Output) ->
+do_update_proj_plt(Args, State, Plt, Output) ->
?INFO("Updating plt...", []),
Files = proj_plt_files(State),
case read_plt(State, Plt) of
{ok, OldFiles} ->
check_plt(State, Plt, Output, OldFiles, Files);
error ->
- build_proj_plt(State, Plt, Output, Files)
+ build_proj_plt(Args, State, Plt, Output, Files)
end.
proj_plt_files(State) ->
@@ -373,8 +377,8 @@ run_plt(State, Plt, Output, Analysis, Files) ->
{files, Files}],
run_dialyzer(State, Opts, Output).
-build_proj_plt(State, Plt, Output, Files) ->
- BasePlt = get_base_plt(State),
+build_proj_plt(Args, State, Plt, Output, Files) ->
+ BasePlt = get_base_plt(Args, State),
?INFO("Updating base plt...", []),
BaseFiles = base_plt_files(State),
{BaseWarnings, State1} = update_base_plt(State, BasePlt, Output, BaseFiles),
@@ -391,10 +395,10 @@ build_proj_plt(State, Plt, Output, Files) ->
throw({dialyzer_error, Error})
end.
-get_base_plt(State) ->
- Prefix = get_config(State, base_plt_prefix, ?PLT_PREFIX),
+get_base_plt(Args, State) ->
+ Prefix = proplists:get_value(base_plt_prefix, Args, get_config(State, base_plt_prefix, ?PLT_PREFIX)),
Name = plt_name(Prefix),
- case get_config(State, base_plt_location, global) of
+ case proplists:get_value(base_plt_location, Args, get_config(State, base_plt_location, global)) of
global ->
GlobalCacheDir = rebar_dir:global_cache_dir(rebar_state:opts(State)),
filename:join(GlobalCacheDir, Name);
@@ -439,21 +443,20 @@ build_plt(State, Plt, Output, Files) ->
{files, Files}],
run_dialyzer(State, Opts, Output).
-succ_typings(State, Plt, Output) ->
- {Args, _} = rebar_state:command_parsed_args(State),
+succ_typings(Args, State, Plt, Output) ->
case proplists:get_value(succ_typings, Args) of
false ->
{0, State};
_ ->
?INFO("Doing success typing analysis...", []),
Files = proj_files(State),
- succ_typings(State, Plt, Output, Files)
+ succ_typings_(State, Plt, Output, Files)
end.
-succ_typings(State, Plt, _, []) ->
+succ_typings_(State, Plt, _, []) ->
?INFO("Analyzing no files with ~p...", [Plt]),
{0, State};
-succ_typings(State, Plt, Output, Files) ->
+succ_typings_(State, Plt, Output, Files) ->
?INFO("Analyzing ~b files with ~p...", [length(Files), Plt]),
Opts = [{analysis_type, succ_typings},
{get_warnings, true},
diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl
index fceb65e..d6d9414 100644
--- a/src/rebar_prv_escriptize.erl
+++ b/src/rebar_prv_escriptize.erl
@@ -63,13 +63,11 @@ desc() ->
do(State) ->
Providers = rebar_state:providers(State),
Cwd = rebar_state:dir(State),
- rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State),
- ?INFO("Building escript...", []),
- Res = case rebar_state:get(State, escript_main_app, undefined) of
+ AppInfo0 = case rebar_state:get(State, escript_main_app, undefined) of
undefined ->
case rebar_state:project_apps(State) of
- [App] ->
- escriptize(State, App);
+ [AppInfo] ->
+ AppInfo;
_ ->
?PRV_ERROR(no_main_app)
end;
@@ -77,13 +75,21 @@ do(State) ->
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
case rebar_app_utils:find(rebar_utils:to_binary(Name), AllApps) of
{ok, AppInfo} ->
- escriptize(State, AppInfo);
+ AppInfo;
_ ->
?PRV_ERROR({bad_name, Name})
end
end,
- rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State),
- Res.
+ case AppInfo0 of
+ {error, _} = Err ->
+ Err;
+ _ ->
+ AppInfo1 = rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo0, State),
+ ?INFO("Building escript...", []),
+ Res = escriptize(State, AppInfo1),
+ rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo1, State),
+ Res
+ end.
escriptize(State0, App) ->
AppName = rebar_app_info:name(App),
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl
index 0b00e89..4764439 100644
--- a/src/rebar_prv_eunit.erl
+++ b/src/rebar_prv_eunit.erl
@@ -424,17 +424,35 @@ resolve_eunit_opts(State) ->
true -> set_verbose(EUnitOpts);
false -> EUnitOpts
end,
- IsVerbose = lists:member(verbose, EUnitOpts1),
- case proplists:get_value(eunit_formatters, EUnitOpts1, not IsVerbose) of
- true -> custom_eunit_formatters(EUnitOpts1);
- false -> EUnitOpts1
+ EUnitOpts2 = case proplists:get_value(profile, Opts, false) of
+ true -> set_profile(EUnitOpts1);
+ false -> EUnitOpts1
+ end,
+ IsVerbose = lists:member(verbose, EUnitOpts2),
+ case proplists:get_value(eunit_formatters, Opts, not IsVerbose) of
+ true -> custom_eunit_formatters(EUnitOpts2);
+ false -> EUnitOpts2
end.
custom_eunit_formatters(Opts) ->
+ ReportOpts = custom_eunit_report_options(Opts),
%% If `report` is already set then treat that like `eunit_formatters` is false
case lists:keymember(report, 1, Opts) of
true -> Opts;
- false -> [no_tty, {report, {eunit_progress, [colored, profile]}} | Opts]
+ false -> [no_tty, {report, {eunit_progress, ReportOpts}} | Opts]
+ end.
+
+custom_eunit_report_options(Opts) ->
+ case lists:member(profile, Opts) of
+ true -> [colored, profile];
+ false -> [colored]
+ end.
+
+set_profile(Opts) ->
+ %% if `profile` is already set don't set it again
+ case lists:member(profile, Opts) of
+ true -> Opts;
+ false -> [profile] ++ Opts
end.
set_verbose(Opts) ->
@@ -508,6 +526,7 @@ eunit_opts(_State) ->
{application, undefined, "application", string, help(app)},
{cover, $c, "cover", boolean, help(cover)},
{cover_export_name, undefined, "cover_export_name", string, help(cover_export_name)},
+ {profile, $p, "profile", boolean, help(profile)},
{dir, $d, "dir", string, help(dir)},
{file, $f, "file", string, help(file)},
{module, $m, "module", string, help(module)},
@@ -521,6 +540,7 @@ eunit_opts(_State) ->
help(app) -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`.";
help(cover) -> "Generate cover data. Defaults to false.";
help(cover_export_name) -> "Base name of the coverdata file to write";
+help(profile) -> "Show the slowest tests. Defaults to false.";
help(dir) -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`.";
help(file) -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`.";
help(module) -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.";
diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl
index c0cfd6d..dd3e6f6 100644
--- a/src/rebar_prv_shell.erl
+++ b/src/rebar_prv_shell.erl
@@ -36,6 +36,7 @@
format_error/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-define(PROVIDER, shell).
-define(DEPS, [compile]).
@@ -503,7 +504,12 @@ find_config(State) ->
no_value ->
no_config;
Filename when is_list(Filename) ->
- rebar_file_utils:consult_config(State, Filename)
+ case is_src_config(Filename) of
+ false ->
+ rebar_file_utils:consult_config(State, Filename);
+ true ->
+ consult_env_config(State, Filename)
+ end
end.
-spec first_value([Fun], State) -> no_value | Value when
@@ -540,5 +546,69 @@ find_config_rebar(State) ->
-spec find_config_relx(rebar_state:t()) -> [tuple()] | no_value.
find_config_relx(State) ->
- debug_get_value(sys_config, rebar_state:get(State, relx, []), no_value,
- "Found config from relx.").
+ %% The order in relx is to load the src version first;
+ %% we do the same.
+ RelxCfg = rebar_state:get(State, relx, []),
+ Src = debug_get_value(sys_config_src, RelxCfg, no_value,
+ "Found config.src from relx."),
+ case Src of
+ no_value ->
+ debug_get_value(sys_config, RelxCfg, no_value,
+ "Found config from relx.");
+ _ ->
+ Src
+ end.
+
+-spec is_src_config(file:filename()) -> boolean().
+is_src_config(Filename) ->
+ filename:extension(Filename) =:= ".src".
+
+-spec consult_env_config(rebar_state:t(), file:filename()) -> [[tuple()]].
+consult_env_config(State, Filename) ->
+ RawString = case file:read_file(Filename) of
+ {error, _} -> "[].";
+ {ok, Bin} -> unicode:characters_to_list(Bin)
+ end,
+ ReplacedStr = replace_env_vars(RawString),
+ case rebar_string:consult(unicode:characters_to_list(ReplacedStr)) of
+ {error, Reason} ->
+ throw(?PRV_ERROR({bad_term_file, Filename, Reason}));
+ [Terms] ->
+ rebar_file_utils:consult_config_terms(State, Terms)
+ end.
+
+%% @doc quick and simple variable substitution writeup.
+%% Supports `${varname}' but not `$varname' nor nested
+%% values such as `${my_${varname}}'.
+%% The variable are also defined as only supporting
+%% the form `[a-zA-Z_]+[a-zA-Z0-9_]*' as per the POSIX
+%% standard.
+-spec replace_env_vars(string()) -> unicode:charlist().
+replace_env_vars("") -> "";
+replace_env_vars("${" ++ Str) ->
+ case until_var_end(Str) of
+ {ok, VarName, Rest} ->
+ replace_varname(VarName) ++ replace_env_vars(Rest);
+ error ->
+ "${" ++ replace_env_vars(Str)
+ end;
+replace_env_vars([Char|Str]) ->
+ [Char | replace_env_vars(Str)].
+
+until_var_end(Str) ->
+ case re:run(Str, "([a-zA-Z_]+[a-zA-Z0-9_]*)}", [{capture, [1], list}]) of
+ nomatch ->
+ error;
+ {match, [Name]} ->
+ {ok, Name, drop_varname(Name, Str)}
+ end.
+
+replace_varname(Var) ->
+ %% os:getenv(Var, "") is only available in OTP-18.0
+ case os:getenv(Var) of
+ false -> "";
+ Val -> Val
+ end.
+
+drop_varname("", "}" ++ Str) -> Str;
+drop_varname([_|Var], [_|Str]) -> drop_varname(Var, Str).
diff --git a/src/rebar_string.erl b/src/rebar_string.erl
index d03b14e..79867f5 100644
--- a/src/rebar_string.erl
+++ b/src/rebar_string.erl
@@ -1,7 +1,12 @@
%%% @doc Compatibility module for string functionality
%%% for pre- and post-unicode support.
+%%%
+%%% Also contains other useful string functionality.
-module(rebar_string).
+%% Compatibility exports
-export([join/2, split/2, lexemes/2, trim/3, uppercase/1, lowercase/1, chr/2]).
+%% Util exports
+-export([consult/1]).
-ifdef(unicode_str).
@@ -42,3 +47,27 @@ uppercase(Str) -> string:to_upper(Str).
lowercase(Str) -> string:to_lower(Str).
chr(Str, Char) -> string:chr(Str, Char).
-endif.
+
+%% @doc
+%% Given a string or binary, parse it into a list of terms, ala file:consult/1
+-spec consult(unicode:chardata()) -> {error, term()} | [term()].
+consult(Str) ->
+ consult([], unicode:characters_to_list(Str), []).
+
+consult(Cont, Str, Acc) ->
+ case erl_scan:tokens(Cont, Str, 0) of
+ {done, Result, Remaining} ->
+ case Result of
+ {ok, Tokens, _} ->
+ case erl_parse:parse_term(Tokens) of
+ {ok, Term} -> consult([], Remaining, [Term | Acc]);
+ {error, Reason} -> {error, Reason}
+ end;
+ {eof, _Other} ->
+ lists:reverse(Acc);
+ {error, Info, _} ->
+ {error, Info}
+ end;
+ {more, Cont1} ->
+ consult(Cont1, eof, Acc)
+ end.
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index 929ca47..bc79db0 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -59,7 +59,7 @@ list_templates(State) ->
%% Expand a single template's value
list_template(Files, {Name, Type, File}, State) ->
- case consult(load_file(Files, Type, File)) of
+ case rebar_string:consult(binary_to_list(load_file(Files, Type, File))) of
{error, Reason} ->
{error, {consult, File, Reason}};
TemplateTerms ->
@@ -158,7 +158,7 @@ drop_var_docs([{K,V}|Rest]) -> [{K,V} | drop_var_docs(Rest)].
%% Load the template index, resolve all variables, and then execute
%% the template.
create({Template, Type, File}, Files, UserVars, Force, State) ->
- TemplateTerms = consult(load_file(Files, Type, File)),
+ TemplateTerms = rebar_string:consult(binary_to_list(load_file(Files, Type, File))),
Vars = drop_var_docs(override_vars(UserVars, get_template_vars(TemplateTerms, State))),
maybe_warn_about_name(Vars),
TemplateCwd = filename:dirname(File),
@@ -394,31 +394,6 @@ load_file(_Files, file, Name) ->
{ok, Bin} = file:read_file(Name),
Bin.
-%% Given a string or binary, parse it into a list of terms, ala file:consult/1
-consult(Str) when is_list(Str) ->
- consult([], Str, []);
-consult(Bin) when is_binary(Bin)->
- consult([], binary_to_list(Bin), []).
-
-consult(Cont, Str, Acc) ->
- case erl_scan:tokens(Cont, Str, 0) of
- {done, Result, Remaining} ->
- case Result of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, Term} -> consult([], Remaining, [Term | Acc]);
- {error, Reason} -> {error, Reason}
- end;
- {eof, _Other} ->
- lists:reverse(Acc);
- {error, Info, _} ->
- {error, Info}
- end;
- {more, Cont1} ->
- consult(Cont1, eof, Acc)
- end.
-
-
write_file(Output, Data, Force) ->
%% determine if the target file already exists
FileExists = filelib:is_regular(Output),