Remove shared state

This commit is contained in:
Tuncer Ayaz 2012-04-22 21:53:32 +02:00
parent 1948eb4a47
commit e185e86bff
16 changed files with 489 additions and 423 deletions

View file

@ -103,9 +103,6 @@ run_aux(Commands) ->
%% Initialize logging system
rebar_log:init(),
%% Initialize vsn cache
_VsnCacheTab = ets:new(rebar_vsn_cache,[named_table, public]),
%% Convert command strings to atoms
CommandAtoms = [list_to_atom(C) || C <- Commands],
@ -118,9 +115,6 @@ run_aux(Commands) ->
%% Note the top-level directory for reference
rebar_config:set_global(base_dir, filename:absname(rebar_utils:get_cwd())),
%% Keep track of how many operations we do, so we can detect bad commands
erlang:put(operations, 0),
%% If $HOME/.rebar/config exists load and use as global config
GlobalConfigFile = filename:join([os:getenv("HOME"), ".rebar", "config"]),
GlobalConfig = case filelib:is_regular(GlobalConfigFile) of
@ -133,8 +127,13 @@ run_aux(Commands) ->
end,
BaseConfig = rebar_config:base_config(GlobalConfig),
%% Keep track of how many operations we do, so we can detect bad commands
BaseConfig1 = rebar_config:set_xconf(BaseConfig, operations, 0),
%% Initialize vsn cache
BaseConfig2 = rebar_config:set_xconf(BaseConfig1, vsn_cache, dict:new()),
%% Process each command, resetting any state between each one
rebar_core:process_commands(CommandAtoms, BaseConfig).
rebar_core:process_commands(CommandAtoms, BaseConfig2).
%%
%% print help/usage string

View file

@ -29,12 +29,12 @@
-export([is_app_dir/0, is_app_dir/1,
is_app_src/1,
app_src_to_app/1,
app_name/1,
app_applications/1,
app_vsn/1,
is_skipped_app/1]).
app_name/2,
app_applications/2,
app_vsn/2,
is_skipped_app/2]).
-export([load_app_file/1]). % TEMPORARY
-export([load_app_file/2]). % TEMPORARY
-include("rebar.hrl").
@ -77,75 +77,80 @@ is_app_src(Filename) ->
app_src_to_app(Filename) ->
filename:join("ebin", filename:basename(Filename, ".app.src") ++ ".app").
app_name(AppFile) ->
case load_app_file(AppFile) of
{ok, AppName, _} ->
AppName;
app_name(Config, AppFile) ->
case load_app_file(Config, AppFile) of
{ok, NewConfig, AppName, _} ->
{NewConfig, AppName};
{error, Reason} ->
?ABORT("Failed to extract name from ~s: ~p\n",
[AppFile, Reason])
end.
app_applications(AppFile) ->
case load_app_file(AppFile) of
{ok, _, AppInfo} ->
get_value(applications, AppInfo, AppFile);
app_applications(Config, AppFile) ->
case load_app_file(Config, AppFile) of
{ok, NewConfig, _, AppInfo} ->
{NewConfig, get_value(applications, AppInfo, AppFile)};
{error, Reason} ->
?ABORT("Failed to extract applications from ~s: ~p\n",
[AppFile, Reason])
end.
app_vsn(AppFile) ->
case load_app_file(AppFile) of
{ok, _, AppInfo} ->
app_vsn(Config, AppFile) ->
case load_app_file(Config, AppFile) of
{ok, Config1, _, AppInfo} ->
AppDir = filename:dirname(filename:dirname(AppFile)),
rebar_utils:vcs_vsn(get_value(vsn, AppInfo, AppFile), AppDir);
rebar_utils:vcs_vsn(Config1, get_value(vsn, AppInfo, AppFile),
AppDir);
{error, Reason} ->
?ABORT("Failed to extract vsn from ~s: ~p\n",
[AppFile, Reason])
end.
is_skipped_app(AppFile) ->
ThisApp = app_name(AppFile),
is_skipped_app(Config, AppFile) ->
{Config1, ThisApp} = app_name(Config, AppFile),
%% Check for apps global parameter; this is a comma-delimited list
%% of apps on which we want to run commands
case get_apps() of
undefined ->
%% No apps parameter specified, check the skip_apps list..
case get_skip_apps() of
undefined ->
%% No skip_apps list, run everything..
false;
SkipApps ->
TargetApps = [list_to_atom(A) ||
A <- string:tokens(SkipApps, ",")],
is_skipped_app(ThisApp, TargetApps)
end;
Apps ->
%% run only selected apps
TargetApps = [list_to_atom(A) || A <- string:tokens(Apps, ",")],
is_selected_app(ThisApp, TargetApps)
end.
Skipped =
case get_apps() of
undefined ->
%% No apps parameter specified, check the skip_apps list..
case get_skip_apps() of
undefined ->
%% No skip_apps list, run everything..
false;
SkipApps ->
TargetApps = [list_to_atom(A) ||
A <- string:tokens(SkipApps, ",")],
is_skipped(ThisApp, TargetApps)
end;
Apps ->
%% run only selected apps
TargetApps = [list_to_atom(A) || A <- string:tokens(Apps, ",")],
is_selected(ThisApp, TargetApps)
end,
{Config1, Skipped}.
%% ===================================================================
%% Internal functions
%% ===================================================================
load_app_file(Filename) ->
load_app_file(Config, Filename) ->
AppFile = {app_file, Filename},
case erlang:get(AppFile) of
undefined ->
case rebar_config:get_xconf(Config, {appfile, AppFile}) of
error ->
case file:consult(Filename) of
{ok, [{application, AppName, AppData}]} ->
erlang:put(AppFile, {AppName, AppData}),
{ok, AppName, AppData};
Config1 = rebar_config:set_xconf(Config,
{appfile, AppFile},
{AppName, AppData}),
{ok, Config1, AppName, AppData};
{error, _} = Error ->
Error;
Other ->
{error, {unexpected_terms, Other}}
end;
{AppName, AppData} ->
{ok, AppName, AppData}
{ok, {AppName, AppData}} ->
{ok, Config, AppName, AppData}
end.
get_value(Key, AppInfo, AppFile) ->
@ -157,7 +162,7 @@ get_value(Key, AppInfo, AppFile) ->
end.
%% apps= for selecting apps
is_selected_app(ThisApp, TargetApps) ->
is_selected(ThisApp, TargetApps) ->
case lists:member(ThisApp, TargetApps) of
false ->
{true, ThisApp};
@ -166,7 +171,7 @@ is_selected_app(ThisApp, TargetApps) ->
end.
%% skip_apps= for filtering apps
is_skipped_app(ThisApp, TargetApps) ->
is_skipped(ThisApp, TargetApps) ->
case lists:member(ThisApp, TargetApps) of
false ->
false;

View file

@ -38,9 +38,9 @@
%% Public API
%% ====================================================================
'generate-appups'(_Config, ReltoolFile) ->
'generate-appups'(Config, ReltoolFile) ->
%% Get the old release path
ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile),
{Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
TargetParentDir = rebar_rel_utils:get_target_parent_dir(ReltoolConfig),
OldVerPath = filename:join([TargetParentDir,
@ -75,7 +75,7 @@
%% Generate appup files for upgraded apps
generate_appup_files(NewVerPath, OldVerPath, UpgradeApps),
ok.
{ok, Config1}.
%% ===================================================================
%% Internal functions

View file

@ -32,13 +32,19 @@
set/3,
set_global/2, get_global/2,
is_verbose/0, get_jobs/0,
set_env/3, get_env/2]).
set_env/3, get_env/2,
set_skip_dir/2, is_skip_dir/2, reset_skip_dirs/1,
clean_config/2,
set_xconf/3, get_xconf/2, erase_xconf/2, reset_xconf/1]).
-include("rebar.hrl").
-record(config, { dir :: file:filename(),
opts = [] :: list(),
envs = new_env() :: dict() }).
envs = new_env() :: dict(),
%% cross-directory config
skip_dirs = new_skip_dirs() :: dict(),
xconf = new_xconf() :: dict() }).
%% Types that can be used from other modules -- alphabetically ordered.
-export_type([config/0]).
@ -46,13 +52,15 @@
%% data types
-opaque config() :: #config{}.
-define(DEFAULT_NAME, "rebar.config").
%% ===================================================================
%% Public API
%% ===================================================================
base_config(#config{opts=Opts0}) ->
ConfName = rebar_config:get_global(config, "rebar.config"),
new(Opts0, ConfName).
base_config(GlobalConfig) ->
ConfName = rebar_config:get_global(config, ?DEFAULT_NAME),
new(GlobalConfig, ConfName).
new() ->
#config{dir = rebar_utils:get_cwd()}.
@ -65,31 +73,8 @@ new(ConfigFile) when is_list(ConfigFile) ->
Other ->
?ABORT("Failed to load ~s: ~p~n", [ConfigFile, Other])
end;
new(_ParentConfig=#config{opts=Opts0})->
new(Opts0, "rebar.config").
new(Opts0, ConfName) ->
%% Load terms from rebar.config, if it exists
Dir = rebar_utils:get_cwd(),
ConfigFile = filename:join([Dir, ConfName]),
Opts = case consult_file(ConfigFile) of
{ok, Terms} ->
%% Found a config file with some terms. We need to
%% be able to distinguish between local definitions
%% (i.e. from the file in the cwd) and inherited
%% definitions. To accomplish this, we use a marker
%% in the proplist (since order matters) between
%% the new and old defs.
Terms ++ [local] ++
[Opt || Opt <- Opts0, Opt /= local];
{error, enoent} ->
[local] ++
[Opt || Opt <- Opts0, Opt /= local];
Other ->
?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other])
end,
#config{dir = Dir, opts = Opts}.
new(_ParentConfig=#config{opts=Opts0, skip_dirs=SkipDirs, xconf=Xconf})->
new(#config{opts=Opts0, skip_dirs=SkipDirs, xconf=Xconf}, ?DEFAULT_NAME).
get(Config, Key, Default) ->
proplists:get_value(Key, Config#config.opts, Default).
@ -145,17 +130,74 @@ consult_file(File) ->
end.
set_env(Config, Mod, Env) ->
OldEnvs = Config#config.envs,
NewEnvs = dict:store(Mod, Env, OldEnvs),
Config#config{envs=NewEnvs}.
NewEnvs = dict:store(Mod, Env, Config#config.envs),
Config#config{envs = NewEnvs}.
get_env(Config, Mod) ->
dict:fetch(Mod, Config#config.envs).
set_skip_dir(Config, Dir) ->
OldSkipDirs = Config#config.skip_dirs,
NewSkipDirs = case is_skip_dir(Config, Dir) of
false ->
?DEBUG("Adding skip dir: ~s\n", [Dir]),
dict:store(Dir, true, OldSkipDirs);
true ->
OldSkipDirs
end,
Config#config{skip_dirs = NewSkipDirs}.
is_skip_dir(Config, Dir) ->
dict:is_key(Dir, Config#config.skip_dirs).
reset_skip_dirs(Config) ->
Config#config{skip_dirs = new_skip_dirs()}.
set_xconf(Config, Key, Value) ->
NewXconf = dict:store(Key, Value, Config#config.xconf),
Config#config{xconf=NewXconf}.
get_xconf(Config, Key) ->
dict:find(Key, Config#config.xconf).
erase_xconf(Config, Key) ->
NewXconf = dict:erase(Key, Config#config.xconf),
Config#config{xconf = NewXconf}.
reset_xconf(Config) ->
Config#config{xconf = new_xconf()}.
clean_config(Old, New) ->
New#config{opts=Old#config.opts}.
%% ===================================================================
%% Internal functions
%% ===================================================================
new(ParentConfig, ConfName) ->
%% Load terms from rebar.config, if it exists
Dir = rebar_utils:get_cwd(),
ConfigFile = filename:join([Dir, ConfName]),
Opts0 = ParentConfig#config.opts,
Opts = case consult_file(ConfigFile) of
{ok, Terms} ->
%% Found a config file with some terms. We need to
%% be able to distinguish between local definitions
%% (i.e. from the file in the cwd) and inherited
%% definitions. To accomplish this, we use a marker
%% in the proplist (since order matters) between
%% the new and old defs.
Terms ++ [local] ++
[Opt || Opt <- Opts0, Opt /= local];
{error, enoent} ->
[local] ++
[Opt || Opt <- Opts0, Opt /= local];
Other ->
?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other])
end,
ParentConfig#config{dir = Dir, opts = Opts}.
consult_and_eval(File, Script) ->
?DEBUG("Evaluating config script ~p~n", [Script]),
ConfigData = try_consult(File),
@ -171,7 +213,8 @@ try_consult(File) ->
{ok, Terms} ->
?DEBUG("Consult config file ~p~n", [File]),
Terms;
{error, enoent} -> [];
{error, enoent} ->
[];
{error, Reason} ->
?ABORT("Failed to read config file ~s: ~p~n", [File, Reason])
end.
@ -188,5 +231,8 @@ local_opts([local | _Rest], Acc) ->
local_opts([Item | Rest], Acc) ->
local_opts(Rest, [Item | Acc]).
new_env() ->
dict:new().
new_env() -> dict:new().
new_skip_dirs() -> dict:new().
new_xconf() -> dict:new().

View file

@ -26,46 +26,17 @@
%% -------------------------------------------------------------------
-module(rebar_core).
-export([process_commands/2,
skip_dir/1,
is_skip_dir/1,
skip_dirs/0]).
-export([process_commands/2]).
-include("rebar.hrl").
%% ===================================================================
%% Public API
%% ===================================================================
skip_dir(Dir) ->
SkipDir = {skip_dir, Dir},
case erlang:get(SkipDir) of
undefined ->
?DEBUG("Adding skip dir: ~s\n", [Dir]),
erlang:put(SkipDir, true);
true ->
ok
end.
is_skip_dir(Dir) ->
case erlang:get({skip_dir, Dir}) of
undefined ->
false;
true ->
true
end.
skip_dirs() ->
[Dir || {{skip_dir, Dir}, true} <- erlang:get()].
%% ===================================================================
%% Internal functions
%% ===================================================================
process_commands([], _ParentConfig) ->
process_commands([], ParentConfig) ->
AbortTrapped = rebar_config:get_global(abort_trapped, false),
case {erlang:get(operations), AbortTrapped} of
case {get_operations(ParentConfig), AbortTrapped} of
{0, _} ->
%% None of the commands had any effect
?ABORT;
@ -76,47 +47,49 @@ process_commands([], _ParentConfig) ->
ok
end;
process_commands([Command | Rest], ParentConfig) ->
try
%% Reset skip dirs
lists:foreach(fun (D) -> erlang:erase({skip_dir, D}) end, skip_dirs()),
Operations = erlang:get(operations),
%% Reset skip dirs
ParentConfig1 = rebar_config:reset_skip_dirs(ParentConfig),
Operations = rebar_config:get_xconf(ParentConfig1, operations),
%% Convert the code path so that all the entries are absolute paths.
%% If not, code:set_path() may choke on invalid relative paths when
%% trying to restore the code path from inside a subdirectory.
true = rebar_utils:expand_code_path(),
_ = process_dir(rebar_utils:get_cwd(), ParentConfig,
Command, sets:new()),
case erlang:get(operations) of
Operations ->
%% This command didn't do anything
?CONSOLE("Command '~p' not understood or not applicable~n",
[Command]);
_ ->
ok
end,
%% Wipe out vsn cache to avoid invalid hits when
%% dependencies are updated
ets:delete_all_objects(rebar_vsn_cache)
catch
throw:rebar_abort ->
case rebar_config:get_global(keep_going, false) of
false ->
?ABORT;
true ->
?WARN("Continuing on after abort: ~p\n", [Rest]),
rebar_config:set_global(abort_trapped, true),
ParentConfig4 =
try
%% Convert the code path so that all the entries are absolute paths.
%% If not, code:set_path() may choke on invalid relative paths when trying
%% to restore the code path from inside a subdirectory.
true = rebar_utils:expand_code_path(),
{ParentConfig2, _DirSet} = process_dir(rebar_utils:get_cwd(),
ParentConfig1, Command,
sets:new()),
case get_operations(ParentConfig2) of
Operations ->
%% This command didn't do anything
?CONSOLE("Command '~p' not understood or not applicable~n",
[Command]);
_ ->
ok
end
end,
process_commands(Rest, ParentConfig).
end,
ParentConfig3 = rebar_config:clean_config(ParentConfig1, ParentConfig2),
%% Wipe out vsn cache to avoid invalid hits when
%% dependencies are updated
rebar_config:set_xconf(ParentConfig3, vsn_cache, dict:new())
catch
throw:rebar_abort ->
case rebar_config:get_global(keep_going, false) of
false ->
?ABORT;
true ->
?WARN("Continuing on after abort: ~p\n", [Rest]),
rebar_config:set_global(abort_trapped, true),
ParentConfig1
end
end,
process_commands(Rest, ParentConfig4).
process_dir(Dir, ParentConfig, Command, DirSet) ->
case filelib:is_dir(Dir) of
false ->
?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]),
DirSet;
{ParentConfig, DirSet};
true ->
ok = file:set_cwd(Dir),
@ -157,13 +130,13 @@ maybe_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath,
maybe_process_dir0(AppFile, ModuleSet, Config, CurrentCodePath,
Dir, Command, DirSet) ->
case rebar_app_utils:is_skipped_app(AppFile) of
{true, SkippedApp} ->
case rebar_app_utils:is_skipped_app(Config, AppFile) of
{Config1, {true, SkippedApp}} ->
?DEBUG("Skipping app: ~p~n", [SkippedApp]),
increment_operations(),
DirSet;
false ->
process_dir0(Dir, Command, DirSet, Config,
Config2 = increment_operations(Config1),
{Config2, DirSet};
{Config1, false} ->
process_dir0(Dir, Command, DirSet, Config1,
CurrentCodePath, ModuleSet)
end.
@ -179,64 +152,63 @@ process_dir0(Dir, Command, DirSet, Config0, CurrentCodePath,
%% Invoke 'preprocess' on the modules -- this yields a list of other
%% directories that should be processed _before_ the current one.
Predirs = acc_modules(Modules, preprocess, Config0, ModuleSetFile),
{Config1, Predirs} = acc_modules(Modules, preprocess, Config0,
ModuleSetFile),
SubdirAssoc = remember_cwd_subdir(Dir, Predirs),
%% Get the list of plug-in modules from rebar.config. These
%% modules may participate in preprocess and postprocess.
{ok, PluginModules} = plugin_modules(Config0, SubdirAssoc),
{ok, PluginModules} = plugin_modules(Config1, SubdirAssoc),
PluginPredirs = acc_modules(PluginModules, preprocess,
Config0, ModuleSetFile),
{Config2, PluginPredirs} = acc_modules(PluginModules, preprocess,
Config1, ModuleSetFile),
AllPredirs = Predirs ++ PluginPredirs,
?DEBUG("Predirs: ~p\n", [AllPredirs]),
DirSet2 = process_each(AllPredirs, Command, Config0,
ModuleSetFile, DirSet),
{Config3, DirSet2} = process_each(AllPredirs, Command, Config2,
ModuleSetFile, DirSet),
%% Make sure the CWD is reset properly; processing the dirs may have
%% caused it to change
ok = file:set_cwd(Dir),
%% Check that this directory is not on the skip list
Config = case is_skip_dir(Dir) of
true ->
%% Do not execute the command on the directory, as some
%% module has requested a skip on it.
?INFO("Skipping ~s in ~s\n", [Command, Dir]),
Config0;
Config7 = case rebar_config:is_skip_dir(Config3, Dir) of
true ->
%% Do not execute the command on the directory, as some
%% module has requested a skip on it.
?INFO("Skipping ~s in ~s\n", [Command, Dir]),
Config3;
false ->
%% Check for and get command specific environments
{Config1, Env} = setup_envs(Config0, Modules),
false ->
%% Check for and get command specific environments
{Config4, Env} = setup_envs(Config3, Modules),
%% Execute any before_command plugins on this directory
execute_pre(Command, PluginModules,
Config1, ModuleSetFile, Env),
%% Execute any before_command plugins on this directory
Config5 = execute_pre(Command, PluginModules,
Config4, ModuleSetFile, Env),
%% Execute the current command on this directory
execute(Command, Modules ++ PluginModules,
Config1, ModuleSetFile, Env),
%% Execute the current command on this directory
Config6 = execute(Command, Modules ++ PluginModules,
Config5, ModuleSetFile, Env),
%% Execute any after_command plugins on this directory
execute_post(Command, PluginModules,
Config1, ModuleSetFile, Env),
Config1
end,
%% Execute any after_command plugins on this directory
execute_post(Command, PluginModules,
Config6, ModuleSetFile, Env)
end,
%% Mark the current directory as processed
DirSet3 = sets:add_element(Dir, DirSet2),
%% Invoke 'postprocess' on the modules. This yields a list of other
%% directories that should be processed _after_ the current one.
Postdirs = acc_modules(Modules ++ PluginModules, postprocess,
Config, ModuleSetFile),
{Config8, Postdirs} = acc_modules(Modules ++ PluginModules, postprocess,
Config7, ModuleSetFile),
?DEBUG("Postdirs: ~p\n", [Postdirs]),
DirSet4 = process_each(Postdirs, Command, Config,
ModuleSetFile, DirSet3),
Res = process_each(Postdirs, Command, Config8,
ModuleSetFile, DirSet3),
%% Make sure the CWD is reset properly; processing the dirs may have
%% caused it to change
@ -246,8 +218,8 @@ process_dir0(Dir, Command, DirSet, Config0, CurrentCodePath,
%% the parent initialized it to
restore_code_path(CurrentCodePath),
%% Return the updated dirset as our result
DirSet4.
%% Return the updated {config, dirset} as our result
Res.
remember_cwd_subdir(Cwd, Subdirs) ->
Store = fun(Dir, Dict) ->
@ -283,16 +255,17 @@ processing_base_dir(Dir) ->
%% Given a list of directories and a set of previously processed directories,
%% process each one we haven't seen yet
%%
process_each([], _Command, _Config, _ModuleSetFile, DirSet) ->
DirSet;
process_each([], _Command, Config, _ModuleSetFile, DirSet) ->
{Config, DirSet};
process_each([Dir | Rest], Command, Config, ModuleSetFile, DirSet) ->
case sets:is_element(Dir, DirSet) of
true ->
?DEBUG("Skipping ~s; already processed!\n", [Dir]),
process_each(Rest, Command, Config, ModuleSetFile, DirSet);
false ->
DirSet2 = process_dir(Dir, Config, Command, DirSet),
process_each(Rest, Command, Config, ModuleSetFile, DirSet2)
{Config1, DirSet2} = process_dir(Dir, Config, Command, DirSet),
Config2 = rebar_config:clean_config(Config, Config1),
process_each(Rest, Command, Config2, ModuleSetFile, DirSet2)
end.
@ -343,22 +316,23 @@ execute(Command, Modules, Config, ModuleFile, Env) ->
false ->
?WARN("'~p' command does not apply to directory ~s\n",
[Command, rebar_utils:get_cwd()])
end;
end,
Config;
TargetModules ->
%% Provide some info on where we are
Dir = rebar_utils:get_cwd(),
?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]),
increment_operations(),
Config1 = increment_operations(Config),
%% Run the available modules
apply_hooks(pre_hooks, Config, Command, Env),
apply_hooks(pre_hooks, Config1, Command, Env),
case catch(run_modules(TargetModules, Command,
Config, ModuleFile)) of
ok ->
apply_hooks(post_hooks, Config, Command, Env),
ok;
Config1, ModuleFile)) of
{ok, NewConfig} ->
apply_hooks(post_hooks, NewConfig, Command, Env),
NewConfig;
{error, failed} ->
?ABORT;
{Module, {error, _} = Other} ->
@ -373,9 +347,13 @@ execute(Command, Modules, Config, ModuleFile, Env) ->
%% Increment the count of operations, since some module
%% responds to this command
increment_operations() ->
erlang:put(operations, erlang:get(operations) + 1).
increment_operations(Config) ->
Operations = get_operations(Config),
rebar_config:set_xconf(Config, operations, Operations + 1).
get_operations(Config) ->
{ok, Operations} = rebar_config:get_xconf(Config, operations),
Operations.
update_code_path(Config) ->
case rebar_config:get_local(Config, lib_dirs, []) of
@ -417,12 +395,14 @@ select_modules([Module | Rest], Command, Acc) ->
select_modules(Rest, Command, Acc)
end.
run_modules([], _Command, _Config, _File) ->
ok;
run_modules([], _Command, Config, _File) ->
{ok, Config};
run_modules([Module | Rest], Command, Config, File) ->
case Module:Command(Config, File) of
ok ->
run_modules(Rest, Command, Config, File);
{ok, NewConfig} ->
run_modules(Rest, Command, NewConfig, File);
{error, _} = Error ->
{Module, Error}
end.
@ -461,11 +441,16 @@ acc_modules(Modules, Command, Config, File) ->
acc_modules(select_modules(Modules, Command, []),
Command, Config, File, []).
acc_modules([], _Command, _Config, _File, Acc) ->
Acc;
acc_modules([], _Command, Config, _File, Acc) ->
{Config, Acc};
acc_modules([Module | Rest], Command, Config, File, Acc) ->
{ok, Dirs} = Module:Command(Config, File),
acc_modules(Rest, Command, Config, File, Acc ++ Dirs).
{Config1, Dirs1} = case Module:Command(Config, File) of
{ok, Dirs} ->
{Config, Dirs};
{ok, NewConfig, Dirs} ->
{NewConfig, Dirs}
end,
acc_modules(Rest, Command, Config1, File, Acc ++ Dirs1).
%%
%% Return a flat list of rebar plugin modules.

View file

@ -57,37 +57,37 @@ preprocess(Config, _) ->
%% Get the list of deps for the current working directory and identify those
%% deps that are available/present.
Deps = rebar_config:get_local(Config, deps, []),
{AvailableDeps, MissingDeps} = find_deps(find, Deps),
{Config1, {AvailableDeps, MissingDeps}} = find_deps(Config, find, Deps),
?DEBUG("Available deps: ~p\n", [AvailableDeps]),
?DEBUG("Missing deps : ~p\n", [MissingDeps]),
%% Add available deps to code path
update_deps_code_path(AvailableDeps),
Config2 = update_deps_code_path(Config1, AvailableDeps),
%% If skip_deps=true, mark each dep dir as a skip_dir w/ the core so that
%% the current command doesn't run on the dep dir. However, pre/postprocess
%% WILL run (and we want it to) for transitivity purposes.
case rebar_config:get_global(skip_deps, false) of
"true" ->
lists:foreach(fun (#dep{dir = Dir}) ->
rebar_core:skip_dir(Dir)
end, AvailableDeps);
_ ->
ok
end,
NewConfig = case rebar_config:get_global(skip_deps, false) of
"true" ->
lists:foldl(
fun(#dep{dir = Dir}, C) ->
rebar_config:set_skip_dir(C, Dir)
end, Config2, AvailableDeps);
_ ->
Config2
end,
%% Return all the available dep directories for process
{ok, [D#dep.dir || D <- AvailableDeps]}.
{ok, NewConfig, dep_dirs(AvailableDeps)}.
postprocess(_Config, _) ->
case erlang:get(?MODULE) of
undefined ->
postprocess(Config, _) ->
case rebar_config:get_xconf(Config, ?MODULE) of
error ->
{ok, []};
Dirs ->
erlang:erase(?MODULE),
{ok, Dirs}
{ok, Dirs} ->
NewConfig = rebar_config:erase_xconf(Config, ?MODULE),
{ok, NewConfig, Dirs}
end.
compile(Config, AppFile) ->
@ -114,11 +114,11 @@ setup_env(_Config) ->
'check-deps'(Config, _) ->
%% Get the list of immediate (i.e. non-transitive) deps that are missing
Deps = rebar_config:get_local(Config, deps, []),
case find_deps(find, Deps) of
{_, []} ->
case find_deps(Config, find, Deps) of
{Config1, {_, []}} ->
%% No missing deps
ok;
{_, MissingDeps} ->
{ok, Config1};
{_Config1, {_, MissingDeps}} ->
lists:foreach(fun (#dep{app=App, vsn_regex=Vsn, source=Src}) ->
?CONSOLE("Dependency not available: "
"~p-~s (~p)\n", [App, Vsn, Src])
@ -129,47 +129,52 @@ setup_env(_Config) ->
'get-deps'(Config, _) ->
%% Determine what deps are available and missing
Deps = rebar_config:get_local(Config, deps, []),
{_AvailableDeps, MissingDeps} = find_deps(find, Deps),
{Config1, {_AvailableDeps, MissingDeps}} = find_deps(Config, find, Deps),
MissingDeps1 = [D || D <- MissingDeps, D#dep.source =/= undefined],
%% For each missing dep with a specified source, try to pull it.
PulledDeps = [use_source(D) || D <- MissingDeps, D#dep.source /= undefined],
{Config2, PulledDeps} =
lists:foldl(fun(D, {C, PulledDeps0}) ->
{C1, D1} = use_source(C, D),
{C1, [D1 | PulledDeps0]}
end, {Config1, []}, MissingDeps1),
%% Add each pulled dep to our list of dirs for post-processing. This yields
%% the necessary transitivity of the deps
erlang:put(?MODULE, [D#dep.dir || D <- PulledDeps]),
ok.
{ok, save_dep_dirs(Config2, lists:reverse(PulledDeps))}.
'update-deps'(Config, _) ->
%% Determine what deps are available and missing
Deps = rebar_config:get_local(Config, deps, []),
UpdatedDeps = [update_source(D) || D <- find_deps(read, Deps),
D#dep.source /= undefined],
%% Determine what deps are required
RawDeps = rebar_config:get_local(Config, deps, []),
{Config1, Deps} = find_deps(Config, read, RawDeps),
%% Update each dep
UpdatedDeps = [update_source(D) || D <- Deps, D#dep.source =/= undefined],
%% Add each updated dep to our list of dirs for post-processing. This yields
%% the necessary transitivity of the deps
erlang:put(?MODULE, [D#dep.dir || D <- UpdatedDeps]),
ok.
{ok, save_dep_dirs(Config1, UpdatedDeps)}.
'delete-deps'(Config, _) ->
%% Delete all the available deps in our deps/ directory, if any
{true, DepsDir} = get_deps_dir(),
Deps = rebar_config:get_local(Config, deps, []),
{AvailableDeps, _} = find_deps(find, Deps),
{Config1, {AvailableDeps, _}} = find_deps(Config, find, Deps),
_ = [delete_dep(D)
|| D <- AvailableDeps,
lists:prefix(DepsDir, D#dep.dir)],
ok.
{ok, Config1}.
'list-deps'(Config, _) ->
Deps = rebar_config:get_local(Config, deps, []),
case find_deps(find, Deps) of
{AvailDeps, []} ->
case find_deps(Config, find, Deps) of
{Config1, {AvailDeps, []}} ->
lists:foreach(fun(Dep) -> print_source(Dep) end, AvailDeps),
ok;
{ok, Config1};
{_, MissingDeps} ->
?ABORT("Missing dependencies: ~p\n", [MissingDeps])
end.
%% ===================================================================
%% Internal functions
%% ===================================================================
@ -191,6 +196,12 @@ get_deps_dir(App) ->
DepsDir = rebar_config:get_global(deps_dir, "deps"),
{true, filename:join([BaseDir, DepsDir, App])}.
dep_dirs(Deps) ->
[D#dep.dir || D <- Deps].
save_dep_dirs(Config, Deps) ->
rebar_config:set_xconf(Config, ?MODULE, dep_dirs(Deps)).
get_lib_dir(App) ->
%% Find App amongst the reachable lib directories
%% Returns either the found path or a tagged tuple with a boolean
@ -200,72 +211,77 @@ get_lib_dir(App) ->
Path -> {true, Path}
end.
update_deps_code_path([]) ->
ok;
update_deps_code_path([Dep | Rest]) ->
case is_app_available(Dep#dep.app, Dep#dep.vsn_regex, Dep#dep.dir) of
{true, _} ->
Dir = filename:join(Dep#dep.dir, "ebin"),
ok = filelib:ensure_dir(filename:join(Dir, "dummy")),
?DEBUG("Adding ~s to code path~n", [Dir]),
true = code:add_patha(Dir);
{false, _} ->
true
end,
update_deps_code_path(Rest).
update_deps_code_path(Config, []) ->
Config;
update_deps_code_path(Config, [Dep | Rest]) ->
Config2 =
case is_app_available(Config, Dep#dep.app,
Dep#dep.vsn_regex, Dep#dep.dir) of
{Config1, {true, _}} ->
Dir = filename:join(Dep#dep.dir, "ebin"),
ok = filelib:ensure_dir(filename:join(Dir, "dummy")),
?DEBUG("Adding ~s to code path~n", [Dir]),
true = code:add_patha(Dir),
Config1;
{Config1, {false, _}} ->
Config1
end,
update_deps_code_path(Config2, Rest).
find_deps(Config, find=Mode, Deps) ->
find_deps(Config, Mode, Deps, {[], []});
find_deps(Config, read=Mode, Deps) ->
find_deps(Config, Mode, Deps, []).
find_deps(find=Mode, Deps) ->
find_deps(Mode, Deps, {[], []});
find_deps(read=Mode, Deps) ->
find_deps(Mode, Deps, []).
find_deps(find, [], {Avail, Missing}) ->
{lists:reverse(Avail), lists:reverse(Missing)};
find_deps(read, [], Deps) ->
lists:reverse(Deps);
find_deps(Mode, [App | Rest], Acc) when is_atom(App) ->
find_deps(Mode, [{App, ".*", undefined} | Rest], Acc);
find_deps(Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) ->
find_deps(Mode, [{App, VsnRegex, undefined} | Rest], Acc);
find_deps(Mode, [{App, VsnRegex, Source} | Rest], Acc) ->
find_deps(Config, find, [], {Avail, Missing}) ->
{Config, {lists:reverse(Avail), lists:reverse(Missing)}};
find_deps(Config, read, [], Deps) ->
{Config, lists:reverse(Deps)};
find_deps(Config, Mode, [App | Rest], Acc) when is_atom(App) ->
find_deps(Config, Mode, [{App, ".*", undefined} | Rest], Acc);
find_deps(Config, Mode, [{App, VsnRegex} | Rest], Acc) when is_atom(App) ->
find_deps(Config, Mode, [{App, VsnRegex, undefined} | Rest], Acc);
find_deps(Config, Mode, [{App, VsnRegex, Source} | Rest], Acc) ->
Dep = #dep { app = App,
vsn_regex = VsnRegex,
source = Source },
{Availability, FoundDir} = find_dep(Dep),
find_deps(Mode, Rest, acc_deps(Mode, Availability, Dep, FoundDir, Acc));
find_deps(_Mode, [Other | _Rest], _Acc) ->
{Config1, {Availability, FoundDir}} = find_dep(Config, Dep),
find_deps(Config1, Mode, Rest,
acc_deps(Mode, Availability, Dep, FoundDir, Acc));
find_deps(_Config, _Mode, [Other | _Rest], _Acc) ->
?ABORT("Invalid dependency specification ~p in ~s\n",
[Other, rebar_utils:get_cwd()]).
find_dep(Dep) ->
find_dep(Config, Dep) ->
%% Find a dep based on its source,
%% e.g. {git, "https://github.com/mochi/mochiweb.git", "HEAD"}
%% Deps with a source must be found (or fetched) locally.
%% Those without a source may be satisfied from lib dir (get_lib_dir).
find_dep(Dep, Dep#dep.source).
find_dep(Config, Dep, Dep#dep.source).
find_dep(Dep, undefined) ->
find_dep(Config, Dep, undefined) ->
%% 'source' is undefined. If Dep is not satisfied locally,
%% go ahead and find it amongst the lib_dir's.
case find_dep_in_dir(Dep, get_deps_dir(Dep#dep.app)) of
{avail, _Dir} = Avail -> Avail;
{missing, _} -> find_dep_in_dir(Dep, get_lib_dir(Dep#dep.app))
case find_dep_in_dir(Config, Dep, get_deps_dir(Dep#dep.app)) of
{_Config1, {avail, _Dir}} = Avail ->
Avail;
{Config1, {missing, _}} ->
find_dep_in_dir(Config1, Dep, get_lib_dir(Dep#dep.app))
end;
find_dep(Dep, _Source) ->
find_dep(Config, Dep, _Source) ->
%% _Source is defined. Regardless of what it is, we must find it
%% locally satisfied or fetch it from the original source
%% into the project's deps
find_dep_in_dir(Dep, get_deps_dir(Dep#dep.app)).
find_dep_in_dir(Config, Dep, get_deps_dir(Dep#dep.app)).
find_dep_in_dir(_Dep, {false, Dir}) ->
{missing, Dir};
find_dep_in_dir(Dep, {true, Dir}) ->
find_dep_in_dir(Config, _Dep, {false, Dir}) ->
{Config, {missing, Dir}};
find_dep_in_dir(Config, Dep, {true, Dir}) ->
App = Dep#dep.app,
VsnRegex = Dep#dep.vsn_regex,
case is_app_available(App, VsnRegex, Dir) of
{true, _AppFile} -> {avail, Dir};
{false, _} -> {missing, Dir}
case is_app_available(Config, App, VsnRegex, Dir) of
{Config1, {true, _AppFile}} -> {Config1, {avail, Dir}};
{Config1, {false, _}} -> {Config1, {missing, Dir}}
end.
acc_deps(find, avail, Dep, AppDir, {Avail, Missing}) ->
@ -288,57 +304,59 @@ require_source_engine(Source) ->
true = source_engine_avail(Source),
ok.
is_app_available(App, VsnRegex, Path) ->
is_app_available(Config, App, VsnRegex, Path) ->
?DEBUG("is_app_available, looking for App ~p with Path ~p~n", [App, Path]),
case rebar_app_utils:is_app_dir(Path) of
{true, AppFile} ->
case rebar_app_utils:app_name(AppFile) of
App ->
Vsn = rebar_app_utils:app_vsn(AppFile),
case rebar_app_utils:app_name(Config, AppFile) of
{Config1, App} ->
{Config2, Vsn} = rebar_app_utils:app_vsn(Config1, AppFile),
?INFO("Looking for ~s-~s ; found ~s-~s at ~s\n",
[App, VsnRegex, App, Vsn, Path]),
case re:run(Vsn, VsnRegex, [{capture, none}]) of
match ->
{true, Path};
{Config2, {true, Path}};
nomatch ->
?WARN("~s has version ~p; requested regex was ~s\n",
[AppFile, Vsn, VsnRegex]),
{false, {version_mismatch,
{AppFile,
{expected, VsnRegex}, {has, Vsn}}}}
{Config2,
{false, {version_mismatch,
{AppFile,
{expected, VsnRegex}, {has, Vsn}}}}}
end;
OtherApp ->
{Config1, OtherApp} ->
?WARN("~s has application id ~p; expected ~p\n",
[AppFile, OtherApp, App]),
{false, {name_mismatch,
{AppFile, {expected, App}, {has, OtherApp}}}}
{Config1,
{false, {name_mismatch,
{AppFile, {expected, App}, {has, OtherApp}}}}}
end;
false ->
?WARN("Expected ~s to be an app dir (containing ebin/*.app), "
"but no .app found.\n", [Path]),
{false, {missing_app_file, Path}}
{Config, {false, {missing_app_file, Path}}}
end.
use_source(Dep) ->
use_source(Dep, 3).
use_source(Config, Dep) ->
use_source(Config, Dep, 3).
use_source(Dep, 0) ->
use_source(_Config, Dep, 0) ->
?ABORT("Failed to acquire source from ~p after 3 tries.\n",
[Dep#dep.source]);
use_source(Dep, Count) ->
use_source(Config, Dep, Count) ->
case filelib:is_dir(Dep#dep.dir) of
true ->
%% Already downloaded -- verify the versioning matches the regex
case is_app_available(Dep#dep.app,
case is_app_available(Config, Dep#dep.app,
Dep#dep.vsn_regex, Dep#dep.dir) of
{true, _} ->
{Config1, {true, _}} ->
Dir = filename:join(Dep#dep.dir, "ebin"),
ok = filelib:ensure_dir(filename:join(Dir, "dummy")),
%% Available version matches up -- we're good to go;
%% add the app dir to our code path
true = code:add_patha(Dir),
Dep;
{false, Reason} ->
{Config1, Dep};
{_Config1, {false, Reason}} ->
%% The app that was downloaded doesn't match up (or had
%% errors or something). For the time being, abort.
?ABORT("Dependency dir ~s failed application validation "
@ -349,7 +367,7 @@ use_source(Dep, Count) ->
require_source_engine(Dep#dep.source),
{true, TargetDir} = get_deps_dir(Dep#dep.app),
download_source(TargetDir, Dep#dep.source),
use_source(Dep#dep { dir = TargetDir }, Count-1)
use_source(Config, Dep#dep { dir = TargetDir }, Count-1)
end.
download_source(AppDir, {hg, Url, Rev}) ->
@ -433,9 +451,6 @@ update_source(AppDir, {bzr, _Url, Rev}) ->
update_source(AppDir, {rsync, Url}) ->
rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]).
%% ===================================================================
%% Source helper functions
%% ===================================================================

View file

@ -45,15 +45,14 @@
%% Public API
%% ===================================================================
%% @doc Generate Erlang program documentation.
-spec doc(Config::rebar_config:config(), File::file:filename()) -> ok.
doc(Config, File) ->
%% Save code path
CodePath = setup_code_path(),
%% Get the edoc_opts and app file info
EDocOpts = rebar_config:get(Config, edoc_opts, []),
{ok, AppName, _AppData} = rebar_app_utils:load_app_file(File),
{ok, Config1, AppName, _AppData} =
rebar_app_utils:load_app_file(Config, File),
%% Determine the age of the summary file
EDocInfoName = filename:join(proplists:get_value(dir, EDocOpts, "doc"),
@ -77,7 +76,7 @@ doc(Config, File) ->
%% Restore code path
true = code:set_path(CodePath),
ok.
{ok, Config1}.
%% ===================================================================
%% Internal functions

View file

@ -36,10 +36,10 @@
%% Public API
%% ===================================================================
escriptize(Config, AppFile) ->
escriptize(Config0, AppFile) ->
%% Extract the application name from the archive -- this is the default
%% name of the generated script
AppName = rebar_app_utils:app_name(AppFile),
{Config, AppName} = rebar_app_utils:app_name(Config0, AppFile),
%% Get the output filename for the escript -- this may include dirs
Filename = rebar_config:get_local(Config, escript_name, AppName),
@ -85,16 +85,17 @@ escriptize(Config, AppFile) ->
%% Finally, update executable perms for our script
{ok, #file_info{mode = Mode}} = file:read_file_info(Filename),
ok = file:change_mode(Filename, Mode bor 8#00111),
ok.
{ok, Config}.
clean(Config, AppFile) ->
clean(Config0, AppFile) ->
%% Extract the application name from the archive -- this is the default
%% name of the generated script
AppName = rebar_app_utils:app_name(AppFile),
{Config, AppName} = rebar_app_utils:app_name(Config0, AppFile),
%% Get the output filename for the escript -- this may include dirs
Filename = rebar_config:get_local(Config, escript_name, AppName),
rebar_file_utils:delete_each([Filename]).
rebar_file_utils:delete_each([Filename]),
{ok, Config}.
%% ===================================================================
%% Internal functions

View file

@ -64,7 +64,7 @@
%% Public API
%% ===================================================================
eunit(Config, _AppFile) ->
eunit(Config0, _AppFile) ->
%% Make sure ?EUNIT_DIR/ and ebin/ directory exists (append dummy module)
ok = filelib:ensure_dir(filename:join(eunit_dir(), "dummy")),
ok = filelib:ensure_dir(filename:join(ebin_dir(), "dummy")),
@ -85,7 +85,7 @@ eunit(Config, _AppFile) ->
%% Copy source files to eunit dir for cover in case they are not directly
%% in src but in a subdirectory of src. Cover only looks in cwd and ../src
%% for source files. Also copy files from src_dirs.
ErlOpts = rebar_utils:erl_opts(Config),
ErlOpts = rebar_utils:erl_opts(Config0),
SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
SrcErls = lists:foldl(
@ -119,8 +119,8 @@ eunit(Config, _AppFile) ->
%% Compile erlang code to ?EUNIT_DIR, using a tweaked config
%% with appropriate defines for eunit, and include all the test modules
%% as well.
rebar_erlc_compiler:doterl_compile(eunit_config(Config),
?EUNIT_DIR, TestErls),
Config = eunit_config(Config0),
rebar_erlc_compiler:doterl_compile(Config, ?EUNIT_DIR, TestErls),
%% Build a list of all the .beams in ?EUNIT_DIR -- use this for
%% cover and eunit testing. Normally you can just tell cover
@ -166,7 +166,7 @@ eunit(Config, _AppFile) ->
%% Restore code path
true = code:set_path(CodePath),
ok.
{ok, Config}.
clean(_Config, _File) ->
rebar_file_utils:rm_rf(?EUNIT_DIR).
@ -218,44 +218,48 @@ get_eunit_opts(Config) ->
BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []).
eunit_config(Config) ->
EqcOpts = eqc_opts(),
PropErOpts = proper_opts(),
{Config1, EqcOpts} = eqc_opts(Config),
{Config2, PropErOpts} = proper_opts(Config1),
ErlOpts = rebar_config:get_list(Config, erl_opts, []),
EunitOpts = rebar_config:get_list(Config, eunit_compile_opts, []),
ErlOpts = rebar_config:get_list(Config2, erl_opts, []),
EunitOpts = rebar_config:get_list(Config2, eunit_compile_opts, []),
Opts0 = [{d, 'TEST'}] ++
ErlOpts ++ EunitOpts ++ EqcOpts ++ PropErOpts,
Opts = [O || O <- Opts0, O =/= no_debug_info],
Config1 = rebar_config:set(Config, erl_opts, Opts),
Config3 = rebar_config:set(Config2, erl_opts, Opts),
FirstErls = rebar_config:get_list(Config1, eunit_first_files, []),
rebar_config:set(Config1, erl_first_files, FirstErls).
FirstErls = rebar_config:get_list(Config3, eunit_first_files, []),
rebar_config:set(Config3, erl_first_files, FirstErls).
eqc_opts() ->
define_if('EQC', is_lib_avail(is_eqc_avail, eqc,
"eqc.hrl", "QuickCheck")).
eqc_opts(Config) ->
{NewConfig, IsAvail} = is_lib_avail(Config, is_eqc_avail, eqc,
"eqc.hrl", "QuickCheck"),
Opts = define_if('EQC', IsAvail),
{NewConfig, Opts}.
proper_opts() ->
define_if('PROPER', is_lib_avail(is_proper_avail, proper,
"proper.hrl", "PropEr")).
proper_opts(Config) ->
{NewConfig, IsAvail} = is_lib_avail(Config, is_proper_avail, proper,
"proper.hrl", "PropEr"),
Opts = define_if('PROPER', IsAvail),
{NewConfig, Opts}.
define_if(Def, true) -> [{d, Def}];
define_if(_Def, false) -> [].
is_lib_avail(DictKey, Mod, Hrl, Name) ->
case erlang:get(DictKey) of
undefined ->
is_lib_avail(Config, DictKey, Mod, Hrl, Name) ->
case rebar_config:get_xconf(Config, DictKey) of
error ->
IsAvail = case code:lib_dir(Mod, include) of
{error, bad_name} ->
false;
Dir ->
filelib:is_regular(filename:join(Dir, Hrl))
end,
erlang:put(DictKey, IsAvail),
NewConfig = rebar_config:set_xconf(Config, DictKey, IsAvail),
?DEBUG("~s availability: ~p\n", [Name, IsAvail]),
IsAvail;
IsAvail ->
IsAvail
{NewConfig, IsAvail};
{ok, IsAvail} ->
{Config, IsAvail}
end.
perform_cover(Config, BeamFiles, SrcModules) ->

View file

@ -39,27 +39,27 @@ compile(Config, File) ->
%% If we get an .app.src file, it needs to be pre-processed and
%% written out as a ebin/*.app file. That resulting file will then
%% be validated as usual.
AppFile = case rebar_app_utils:is_app_src(File) of
true ->
preprocess(Config, File);
false ->
File
end,
{Config1, AppFile} = case rebar_app_utils:is_app_src(File) of
true ->
preprocess(Config, File);
false ->
{Config, File}
end,
%% Load the app file and validate it.
case rebar_app_utils:load_app_file(AppFile) of
{ok, AppName, AppData} ->
case rebar_app_utils:load_app_file(Config1, AppFile) of
{ok, Config2, AppName, AppData} ->
validate_name(AppName, AppFile),
%% In general, the list of modules is an important thing to validate
%% for compliance with OTP guidelines and upgrade procedures.
%% However, some people prefer not to validate this list.
case rebar_config:get_local(Config, validate_app_modules, true) of
case rebar_config:get_local(Config1, validate_app_modules, true) of
true ->
validate_modules(AppName,
proplists:get_value(modules, AppData));
Modules = proplists:get_value(modules, AppData),
{validate_modules(AppName, Modules), Config2};
false ->
ok
{ok, Config2}
end;
{error, Reason} ->
?ABORT("Failed to load app file ~s: ~p\n", [AppFile, Reason])
@ -88,17 +88,17 @@ clean(_Config, File) ->
%% ===================================================================
preprocess(Config, AppSrcFile) ->
case rebar_app_utils:load_app_file(AppSrcFile) of
{ok, AppName, AppData} ->
case rebar_app_utils:load_app_file(Config, AppSrcFile) of
{ok, Config1, AppName, AppData} ->
%% Look for a configuration file with vars we want to
%% substitute. Note that we include the list of modules available in
%% ebin/ and update the app data accordingly.
AppVars = load_app_vars(Config) ++ [{modules, ebin_modules()}],
AppVars = load_app_vars(Config1) ++ [{modules, ebin_modules()}],
A1 = apply_app_vars(AppVars, AppData),
%% AppSrcFile may contain instructions for generating a vsn number
Vsn = rebar_app_utils:app_vsn(AppSrcFile),
{Config2, Vsn} = rebar_app_utils:app_vsn(Config1, AppSrcFile),
A2 = lists:keystore(vsn, 1, A1, {vsn, Vsn}),
%% Build the final spec as a string
@ -112,7 +112,7 @@ preprocess(Config, AppSrcFile) ->
%% on the code path
true = code:add_path(filename:absname(filename:dirname(AppFile))),
AppFile;
{Config2, AppFile};
{error, Reason} ->
?ABORT("Failed to read ~s for preprocessing: ~p\n",

View file

@ -96,14 +96,14 @@
objects = [] :: [file:filename(), ...] | [],
opts = [] ::list() | []}).
compile(Config, AppFile) ->
rebar_utils:deprecated(port_sources, port_specs, Config, "soon"),
rebar_utils:deprecated(so_name, port_specs, Config, "soon"),
rebar_utils:deprecated(so_specs, port_specs, Config, "soon"),
compile(Config0, AppFile) ->
rebar_utils:deprecated(port_sources, port_specs, Config0, "soon"),
rebar_utils:deprecated(so_name, port_specs, Config0, "soon"),
rebar_utils:deprecated(so_specs, port_specs, Config0, "soon"),
%% TODO: remove SpecType and OldSources make get_specs/2
%% return list(#spec{}) when removing deprecated options
{SpecType, {OldSources, Specs}} = get_specs(Config, AppFile),
{Config, {SpecType, {OldSources, Specs}}} = get_specs(Config0, AppFile),
case {SpecType, OldSources, Specs} of
{old, [], _} ->
@ -149,7 +149,7 @@ compile(Config, AppFile) ->
clean(Config, AppFile) ->
%% TODO: remove SpecType and OldSources make get_specs/2
%% return list(#spec{}) when removing deprecated options
{SpecType, {OldSources, Specs}} = get_specs(Config, AppFile),
{Config1, {SpecType, {OldSources, Specs}}} = get_specs(Config, AppFile),
case {SpecType, OldSources, Specs} of
{old, [], _} ->
@ -163,7 +163,8 @@ clean(Config, AppFile) ->
rebar_file_utils:delete_each([Target]),
rebar_file_utils:delete_each(Objects)
end, Specs)
end.
end,
{ok, Config1}.
setup_env(Config) ->
setup_env(Config, []).
@ -264,9 +265,10 @@ get_specs(Config, AppFile) ->
case rebar_config:get_local(Config, port_specs, undefined) of
undefined ->
%% TODO: DEPRECATED: remove support for non-port_specs syntax
{old, old_get_specs(Config, AppFile)};
{Config1, Specs} = old_get_specs(Config, AppFile),
{Config1, {old, Specs}};
PortSpecs ->
{new, get_port_specs(Config, PortSpecs)}
{Config, {new, get_port_specs(Config, PortSpecs)}}
end.
get_port_specs(Config, PortSpecs) ->
@ -327,31 +329,32 @@ switch_to_dll_or_exe(Target) ->
old_get_specs(Config, AppFile) ->
OsType = os:type(),
SourceFiles = old_get_sources(Config),
Specs =
{NewConfig, Specs} =
case rebar_config:get_local(Config, so_specs, undefined) of
undefined ->
Objects = port_objects(SourceFiles),
%% New form of so_specs is not provided. See if the old form
%% of {so_name} is available instead
Dir = "priv",
SoName =
{Config2, SoName} =
case rebar_config:get_local(Config, so_name, undefined) of
undefined ->
%% Ok, neither old nor new form is
%% available. Use the app name and
%% generate a sensible default.
AppName = rebar_app_utils:app_name(AppFile),
{Config1, AppName} =
rebar_app_utils:app_name(Config, AppFile),
DrvName = ?FMT("~s_drv.so", [AppName]),
filename:join([Dir, DrvName]);
{Config1, filename:join([Dir, DrvName])};
AName ->
%% Old form is available -- use it
filename:join(Dir, AName)
{Config, filename:join(Dir, AName)}
end,
[old_get_so_spec({SoName, Objects}, OsType)];
{Config2, [old_get_so_spec({SoName, Objects}, OsType)]};
SoSpecs ->
[old_get_so_spec(S, OsType) || S <- SoSpecs]
{Config, [old_get_so_spec(S, OsType) || S <- SoSpecs]}
end,
{SourceFiles, Specs}.
{NewConfig, {SourceFiles, Specs}}.
old_get_sources(Config) ->
RawSources = rebar_config:get_local(Config, port_sources,

View file

@ -35,7 +35,7 @@
get_rel_apps/2,
get_previous_release_path/0,
get_rel_file_path/2,
load_config/1,
load_config/2,
get_sys_tuple/1,
get_target_dir/1,
get_root_dir/1,
@ -123,10 +123,10 @@ get_previous_release_path() ->
%%
%% Load terms from reltool.config
%%
load_config(ReltoolFile) ->
load_config(Config, ReltoolFile) ->
case rebar_config:consult_file(ReltoolFile) of
{ok, Terms} ->
expand_version(Terms, filename:dirname(ReltoolFile));
expand_version(Config, Terms, filename:dirname(ReltoolFile));
Other ->
?ABORT("Failed to load expected config from ~s: ~p\n",
[ReltoolFile, Other])
@ -217,16 +217,23 @@ make_proplist([H|T], Acc) ->
make_proplist([], Acc) ->
Acc.
expand_version(ReltoolConfig, Dir) ->
expand_version(Config, ReltoolConfig, Dir) ->
case lists:keyfind(sys, 1, ReltoolConfig) of
{sys, Sys} ->
ExpandedSys = {sys, [expand_rel_version(Term, Dir) || Term <- Sys]},
lists:keyreplace(sys, 1, ReltoolConfig, ExpandedSys);
{Config1, Rels} =
lists:foldl(
fun(Term, {C, R}) ->
{C1, Rel} = expand_rel_version(C, Term, Dir),
{C1, [Rel|R]}
end, {Config, []}, Sys),
ExpandedSys = {sys, lists:reverse(Rels)},
{Config1, lists:keyreplace(sys, 1, ReltoolConfig, ExpandedSys)};
_ ->
ReltoolConfig
{Config, ReltoolConfig}
end.
expand_rel_version({rel, Name, Version, Apps}, Dir) ->
{rel, Name, rebar_utils:vcs_vsn(Version, Dir), Apps};
expand_rel_version(Other, _Dir) ->
Other.
expand_rel_version(Config, {rel, Name, Version, Apps}, Dir) ->
{NewConfig, VsnString} = rebar_utils:vcs_vsn(Config, Version, Dir),
{NewConfig, {rel, Name, VsnString, Apps}};
expand_rel_version(Config, Other, _Dir) ->
{Config, Other}.

View file

@ -37,12 +37,12 @@
%% Public API
%% ===================================================================
generate(Config, ReltoolFile) ->
generate(Config0, ReltoolFile) ->
%% Make sure we have decent version of reltool available
check_vsn(),
%% Load the reltool configuration from the file
ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile),
{Config, ReltoolConfig} = rebar_rel_utils:load_config(Config0, ReltoolFile),
Sys = rebar_rel_utils:get_sys_tuple(ReltoolConfig),
@ -56,7 +56,7 @@ generate(Config, ReltoolFile) ->
%% Finally, run reltool
case catch(run_reltool(Server, Config, ReltoolConfig)) of
ok ->
ok;
{ok, Config};
{error, failed} ->
?ABORT;
Other2 ->
@ -64,18 +64,17 @@ generate(Config, ReltoolFile) ->
?ABORT
end.
overlay(_Config, ReltoolFile) ->
overlay(Config, ReltoolFile) ->
%% Load the reltool configuration from the file
ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile),
process_overlay(ReltoolConfig).
{Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
{Config1, process_overlay(ReltoolConfig)}.
clean(_Config, ReltoolFile) ->
ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile),
clean(Config, ReltoolFile) ->
{Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
TargetDir = rebar_rel_utils:get_target_dir(ReltoolConfig),
rebar_file_utils:rm_rf(TargetDir),
rebar_file_utils:delete_each(["reltool.spec"]).
rebar_file_utils:delete_each(["reltool.spec"]),
{ok, Config1}.
%% ===================================================================
%% Internal functions

View file

@ -40,7 +40,7 @@ preprocess(Config, _) ->
Cwd = rebar_utils:get_cwd(),
ListSubdirs = rebar_config:get_local(Config, sub_dirs, []),
Subdirs0 = lists:flatmap(fun filelib:wildcard/1, ListSubdirs),
case {rebar_core:is_skip_dir(Cwd), Subdirs0} of
case {rebar_config:is_skip_dir(Config, Cwd), Subdirs0} of
{true, []} ->
{ok, []};
{true, _} ->

View file

@ -38,9 +38,9 @@
%% Public API
%% ====================================================================
'generate-upgrade'(_Config, ReltoolFile) ->
'generate-upgrade'(Config0, ReltoolFile) ->
%% Get the old release path
ReltoolConfig = rebar_rel_utils:load_config(ReltoolFile),
{Config, ReltoolConfig} = rebar_rel_utils:load_config(Config0, ReltoolFile),
TargetParentDir = rebar_rel_utils:get_target_parent_dir(ReltoolConfig),
TargetDir = rebar_rel_utils:get_target_dir(ReltoolConfig),
@ -72,7 +72,7 @@
%% Restore original path
true = code:set_path(OrigPath),
ok.
{ok, Config}.
%% ===================================================================
%% Internal functions

View file

@ -42,7 +42,7 @@
prop_check/3,
expand_code_path/0,
expand_env_variable/3,
vcs_vsn/2,
vcs_vsn/3,
deprecated/3, deprecated/4,
get_deprecated_global/3, get_deprecated_global/4,
get_deprecated_list/4, get_deprecated_list/5,
@ -198,14 +198,17 @@ expand_env_variable(InStr, VarName, RawVarValue) ->
re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts)
end.
vcs_vsn(Vcs, Dir) ->
vcs_vsn(Config, Vcs, Dir) ->
Key = {Vcs, Dir},
try ets:lookup_element(rebar_vsn_cache, Key, 2)
catch
error:badarg ->
{ok, Cache} = rebar_config:get_xconf(Config, vsn_cache),
case dict:find(Key, Cache) of
error ->
VsnString = vcs_vsn_1(Vcs, Dir),
ets:insert(rebar_vsn_cache, {Key, VsnString}),
VsnString
Cache1 = dict:store(Key, VsnString, Cache),
Config1 = rebar_config:set_xconf(Config, vsn_cache, Cache1),
{Config1, VsnString};
{ok, VsnString} ->
{Config, VsnString}
end.
get_deprecated_global(OldOpt, NewOpt, When) ->