Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: juranki/rebar
...
head fork: juranki/rebar
Checking mergeability… Don't worry, you can still create the pull request.
  • 14 commits
  • 17 files changed
  • 0 commit comments
  • 8 contributors
Commits on Feb 15, 2011
@norton norton Clarify trailing slash for PIPE_DIR
The trailing slash for PIPE_DIR is necessary for
both start and attach operations.
1979da9
@hyperthunk hyperthunk Add common_test suite template
This change adds a simple common_test suite template
that can be instantiated with the name of a module
under test like so:

`rebar create template=ctsuite testmod=mymodule`

The template creates an empty test suite in the test
directory, automatically exports test functions and
sets up a first, skipped test function.
7810d7b
@tuncer tuncer Remove gratuitous spaces 1b10807
@nox nox Give an absolute path to code:add_path/1
If an app uses -include_lib for its own included files, compilation
fails if the app directory isn't in $ERL_LIBS because code:lib_dir/1
will return an error. An absolute path needs to be added to code path
instead of just "ebin".
9ee8ed9
joewilliams Add 'generate-appups' command
To further support OTP releases I have added support for generating
application appup files. These include instructions that systools uses
to generate a relup file which contains the low level instructions
needed to perform a hot code upgrade. My goal with this module is to
produce "good enough" appup files or at least a skeleton to help one get
started with something more complex. If an appup file already exists for
an application this command will not attempt to create a new one.

Usage:
$ rebar generate-appups previous_release=/path/to/old/version

Generally this command will be run just before 'generate-upgrade'.
11bf6b4
Commits on Feb 16, 2011
joewilliams Fix generate-appups regression (atom vs list) c6da0b3
@tuncer tuncer Add PropEr support 377c9e8
Commits on Feb 17, 2011
joewilliams Clean up rebar_appups and rebar_upgrade 4e8dcfb
joewilliams Clean up trailing whitespace 1e9b2b8
@tuncer tuncer Add Anders 'andekar' to THANKS file 0a06a53
@kocolosk kocolosk Expand {vsn,git} in app.src to git-describe output b0860da
@tuncer tuncer Add {vsn,Vcs} support for bzr, hg and svn 01a7473
Commits on Feb 18, 2011
@dizzyd dizzyd Clean up and simplify {vsn, VCS} support 6056c63
Commits on Feb 20, 2011
@juranki Add a way to run tests to generate config values
Write module 'configure' (configure.erl) and place it in the same
directory with rebar.config.

In rebar config, specify values of form
  {test, Fun, Args}
  Fun = atom()
  Args = list()

When rebar is run, the forms will be replaced with values returned
by erlang:apply(configure, Fun, Args).

Values will be cached in rebar.cache and the cache is refreshed when
rebar.config or configure.erl modification timestamps are newer
than rebar.cache.
0d77e59
View
1  THANKS
@@ -42,3 +42,4 @@ Daniel Reverri
Jesper Louis Andersen
Richard Jones
Tim Watson
+Anders 'andekar'
View
2  ebin/rebar.app
@@ -3,6 +3,7 @@
{vsn, "2"},
{modules, [ rebar,
rebar_abnfc_compiler,
+ rebar_appups,
rebar_app_utils,
rebar_base_compiler,
rebar_config,
@@ -80,6 +81,7 @@
]},
{rel_dir, [
+ rebar_appups,
rebar_reltool,
rebar_upgrade
]}
View
167 priv/templates/ctsuite.erl
@@ -0,0 +1,167 @@
+%% common_test suite for {{testmod}}
+
+-module({{testmod}}_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%%
+%% Info = [tuple()]
+%% List of key/value pairs.
+%%
+%% Description: Returns list of tuples to set default properties
+%% for the suite.
+%%
+%% Note: The suite/0 function is only meant to be used to return
+%% default data values, not perform any other operations.
+%%--------------------------------------------------------------------
+suite() -> [{timetrap, {seconds, 20}}].
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%%
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% The name of the group.
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% Group properties that may be combined.
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% The name of a test case.
+%% Shuffle = shuffle | {shuffle,Seed}
+%% To get cases executed in random order.
+%% Seed = {integer(),integer(),integer()}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% To get execution of cases repeated.
+%% N = integer() | forever
+%%
+%% Description: Returns a list of test case group definitions.
+%%--------------------------------------------------------------------
+groups() -> [].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases
+%%
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% Name of a test case group.
+%% TestCase = atom()
+%% Name of a test case.
+%%
+%% Description: Returns the list of groups and test cases that
+%% are to be executed.
+%%
+%% NB: By default, we export all 1-arity user defined functions
+%%--------------------------------------------------------------------
+all() ->
+ [ {exports, Functions} | _ ] = ?MODULE:module_info(),
+ [ FName || {FName, _} <- lists:filter(
+ fun ({module_info,_}) -> false;
+ ({all,_}) -> false;
+ ({init_per_suite,1}) -> false;
+ ({end_per_suite,1}) -> false;
+ ({_,1}) -> true;
+ ({_,_}) -> false
+ end, Functions)].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the suite.
+%%
+%% Description: Initialization before the suite.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after the suite.
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% GroupName = atom()
+%% Name of the test case group that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding configuration data for the group.
+%% Reason = term()
+%% The reason for skipping all test cases and subgroups in the group.
+%%
+%% Description: Initialization before each test case group.
+%%--------------------------------------------------------------------
+init_per_group(_group, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%%
+%% GroupName = atom()
+%% Name of the test case group that is finished.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding configuration data for the group.
+%%
+%% Description: Cleanup after each test case group.
+%%--------------------------------------------------------------------
+end_per_group(_group, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Initialization before each test case.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%%
+%% TestCase = atom()
+%% Name of the test case that is finished.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for failing the test case.
+%%
+%% Description: Cleanup after each test case.
+%%--------------------------------------------------------------------
+end_per_testcase(TestCase, Config) ->
+ Config.
+
+test_{{testmod}}() ->
+ [{userdata,[{doc,"Testing the {{testmod}} module"}]}].
+
+test_{{testmod}}(_Config) ->
+ {skip,"Not implemented."}.
View
2  priv/templates/ctsuite.template
@@ -0,0 +1,2 @@
+{variables, [{testmod, "mymodule"}]}.
+{template, "ctsuite.erl", "test/{{testmod}}_SUITE.erl"}.
View
4 priv/templates/simplenode.runner
@@ -7,6 +7,7 @@ RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd)
RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}
RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc
RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log
+# Note the trailing slash on $PIPE_DIR/
PIPE_DIR=/tmp/$RUNNER_BASE_DIR/
RUNNER_USER=
@@ -61,8 +62,7 @@ case "$1" in
HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start"
export HEART_COMMAND
mkdir -p $PIPE_DIR
- # Note the trailing slash on $PIPE_DIR/
- $ERTS_PATH/run_erl -daemon $PIPE_DIR/ $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console" 2>&1
+ $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console" 2>&1
;;
stop)
View
6 src/rebar.erl
@@ -224,6 +224,8 @@ generate [dump_spec=0/1] Build release with reltool
generate-upgrade previous_release=path Build an upgrade package
+generate-appups previous_release=path Generate appup files
+
eunit [suite=foo] Run eunit [test/foo_tests.erl] tests
ct [suite=] [case=] Run common_test suites in ./test
@@ -277,8 +279,8 @@ filter_flags([Item | Rest], Commands) ->
command_names() ->
["build-plt", "check-deps", "check-plt", "clean", "compile", "create",
"create-app", "create-node", "ct", "delete-deps", "dialyze", "doc",
- "eunit", "generate", "generate-upgrade", "get-deps", "help",
- "list-templates", "update-deps", "version", "xref"].
+ "eunit", "generate", "generate-appups", "generate-upgrade", "get-deps",
+ "help", "list-templates", "update-deps", "version", "xref"].
unabbreviate_command_names([]) ->
[];
View
17 src/rebar_app_utils.erl
@@ -89,7 +89,7 @@ app_applications(AppFile) ->
app_vsn(AppFile) ->
case load_app_file(AppFile) of
{ok, _, AppInfo} ->
- get_value(vsn, AppInfo, AppFile);
+ vcs_vsn(get_value(vsn, AppInfo, AppFile));
{error, Reason} ->
?ABORT("Failed to extract vsn from ~s: ~p\n",
[AppFile, Reason])
@@ -124,3 +124,18 @@ get_value(Key, AppInfo, AppFile) ->
Value ->
Value
end.
+
+vcs_vsn(Vcs) ->
+ case vcs_vsn_cmd(Vcs) of
+ {unknown, VsnString} ->
+ VsnString;
+ Cmd ->
+ {ok, VsnString} = rebar_utils:sh(Cmd, [{use_stdout, false}]),
+ string:strip(VsnString, right, $\n)
+ end.
+
+vcs_vsn_cmd(git) -> "git describe --always --tags";
+vcs_vsn_cmd(hg) -> "hg identify -i";
+vcs_vsn_cmd(bzr) -> "bzr revno";
+vcs_vsn_cmd(svn) -> "svnversion";
+vcs_vsn_cmd(Version) -> {unknown, Version}.
View
179 src/rebar_appups.erl
@@ -0,0 +1,179 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% Copyright (c) 2011 Joe Williams (joe@joetify.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.
+%% ------------------------------------------------------------------
+
+-module(rebar_appups).
+
+-include("rebar.hrl").
+
+-export(['generate-appups'/2]).
+
+-define(APPUPFILEFORMAT, "%% appup generated for ~p by rebar (~p)~n"
+ "{~p, [{~p, ~p}], [{~p, []}]}.~n").
+
+%% ====================================================================
+%% Public API
+%% ====================================================================
+
+'generate-appups'(_Config, ReltoolFile) ->
+ %% Get the old release path
+ OldVerPath = rebar_rel_utils:get_previous_release_path(),
+
+ %% Get the new and old release name and versions
+ {Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolFile),
+ NewVerPath = filename:join([".", Name]),
+ {NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NewVerPath),
+ {OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath),
+
+ %% Run some simple checks
+ true = rebar_utils:prop_check(NewVer =/= OldVer,
+ "New and old .rel versions match~n", []),
+ true = rebar_utils:prop_check(
+ NewName == OldName,
+ "Reltool and .rel release names do not match~n", []),
+
+ %% Get lists of the old and new app files
+ OldAppFiles = rebar_utils:find_files(
+ filename:join([OldVerPath, "lib"]), "^.*.app$"),
+ NewAppFiles = rebar_utils:find_files(
+ filename:join([NewName, "lib"]), "^.*.app$"),
+
+ %% Find all the apps that have been upgraded
+ UpgradedApps = get_upgraded_apps(OldAppFiles, NewAppFiles),
+
+ %% Get a list of any appup files that exist in the new release
+ NewAppUpFiles = rebar_utils:find_files(
+ filename:join([NewName, "lib"]), "^.*.appup$"),
+
+ %% Convert the list of appup files into app names
+ AppUpApps = lists:map(fun(File) ->
+ file_to_name(File)
+ end, NewAppUpFiles),
+
+ %% Create a list of apps that don't already have appups
+ Apps = genappup_which_apps(UpgradedApps, AppUpApps),
+
+ %% Generate appup files
+ generate_appup_files(Name, OldVerPath, Apps),
+
+ ok.
+
+%% ===================================================================
+%% Internal functions
+%% ===================================================================
+
+get_upgraded_apps(OldAppFiles, NewAppFiles) ->
+ OldAppsVer = [{rebar_app_utils:app_name(AppFile),
+ rebar_app_utils:app_vsn(AppFile)} || AppFile <- OldAppFiles],
+ NewAppsVer = [{rebar_app_utils:app_name(AppFile),
+ rebar_app_utils:app_vsn(AppFile)} || AppFile <- NewAppFiles],
+ UpgradedApps = lists:subtract(NewAppsVer, OldAppsVer),
+ lists:map(
+ fun({App, NewVer}) ->
+ {App, OldVer} = proplists:lookup(App, OldAppsVer),
+ {App, {OldVer, NewVer}}
+ end,
+ UpgradedApps).
+
+file_to_name(File) ->
+ filename:rootname(filename:basename(File)).
+
+genappup_which_apps(UpgradedApps, [First|Rest]) ->
+ List = proplists:delete(list_to_atom(First), UpgradedApps),
+ genappup_which_apps(List, Rest);
+genappup_which_apps(Apps, []) ->
+ Apps.
+
+generate_appup_files(Name, OldVerPath, [{App, {OldVer, NewVer}}|Rest]) ->
+ OldEbinDir = filename:join([".", OldVerPath, "lib",
+ atom_to_list(App) ++ "-" ++ OldVer, "ebin"]),
+ NewEbinDir = filename:join([".", Name, "lib",
+ atom_to_list(App) ++ "-" ++ NewVer, "ebin"]),
+
+ {AddedFiles, DeletedFiles, ChangedFiles} = beam_lib:cmp_dirs(NewEbinDir,
+ OldEbinDir),
+
+ Added = [generate_instruction(added, File) || File <- AddedFiles],
+ Deleted = [generate_instruction(deleted, File) || File <- DeletedFiles],
+ Changed = [generate_instruction(changed, File) || File <- ChangedFiles],
+
+ Inst = lists:append([Added, Deleted, Changed]),
+
+ AppUpFile = filename:join([NewEbinDir, atom_to_list(App) ++ ".appup"]),
+
+ ok = file:write_file(AppUpFile,
+ io_lib:fwrite(?APPUPFILEFORMAT,
+ [App, rebar_utils:now_str(), NewVer,
+ OldVer, Inst, OldVer])),
+
+ ?CONSOLE("Generated appup for ~p~n", [App]),
+ generate_appup_files(Name, OldVerPath, Rest);
+generate_appup_files(_, _, []) ->
+ ?CONSOLE("Appup generation complete~n", []).
+
+generate_instruction(added, File) ->
+ Name = list_to_atom(file_to_name(File)),
+ {add_module, Name};
+generate_instruction(deleted, File) ->
+ Name = list_to_atom(file_to_name(File)),
+ {delete_module, Name};
+generate_instruction(changed, {File, _}) ->
+ {ok, {Name, List}} = beam_lib:chunks(File, [attributes, exports]),
+ Behavior = get_behavior(List),
+ CodeChange = is_code_change(List),
+ generate_instruction_advanced(Name, Behavior, CodeChange).
+
+generate_instruction_advanced(Name, undefined, undefined) ->
+ %% Not a behavior or code change, assume purely functional
+ {load_module, Name};
+generate_instruction_advanced(Name, [supervisor], _) ->
+ %% Supervisor
+ {update, Name, supervisor};
+generate_instruction_advanced(Name, _, code_change) ->
+ %% Includes code_change export
+ {update, Name, {advanced, []}};
+generate_instruction_advanced(Name, _, _) ->
+ %% Anything else
+ {update, Name}.
+
+get_behavior(List) ->
+ Attributes = proplists:get_value(attributes, List),
+ Behavior = case proplists:get_value(behavior, Attributes) of
+ undefined ->
+ proplists:get_value(behaviour, Attributes);
+ Else ->
+ Else
+ end,
+ Behavior.
+
+is_code_change(List) ->
+ Exports = proplists:get_value(exports, List),
+ case proplists:is_defined(code_change, Exports) of
+ true ->
+ code_change;
+ false ->
+ undefined
+ end.
View
96 src/rebar_config.erl
@@ -60,13 +60,19 @@ new(ParentConfig) ->
ConfigFile = filename:join([Dir, ConfName]),
Opts = case file:consult(ConfigFile) of
{ok, Terms} ->
- %% Found a config file with some terms. We need to
+ %% Found a config file with some terms.
+
+ %% Replace terms of form {test, ...} with values generated
+ %% by functions in configure.erl
+ Terms2 = apply_platform_tests(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] ++
+ Terms2 ++ [local] ++
[Opt || Opt <- ParentConfig#config.opts, Opt /= local];
{error, enoent} ->
[local] ++
@@ -124,3 +130,89 @@ local_opts([local | _Rest], Acc) ->
lists:reverse(Acc);
local_opts([Item | Rest], Acc) ->
local_opts(Rest, [Item | Acc]).
+
+apply_platform_tests(Terms) ->
+ case find_platform_tests(Terms, []) of
+ [] ->
+ Terms;
+ Tests ->
+ case cache_status() of
+ create ->
+ ?CONSOLE("Creating platform configuration cache.~n", []),
+ ok = create_platform_cache(Tests);
+ update ->
+ ?CONSOLE("Updating platform configuration cache.~n", []),
+ ok = file:delete("rebar.cache"),
+ ok = create_platform_cache(Tests);
+ ok ->
+ ok
+ end,
+ replace_platform_values(Terms)
+ end.
+
+find_platform_tests([], Acc) ->
+ Acc;
+find_platform_tests([{test, Fun, Args} | Rest], Acc) ->
+ Acc2 = case lists:member({Fun, Args}, Acc) of
+ true ->
+ Acc;
+ false ->
+ [{Fun, Args} | Acc]
+ end,
+ find_platform_tests(Rest, Acc2);
+find_platform_tests([Term | Rest], Acc) when is_list(Term) ->
+ find_platform_tests(Term ++ Rest, Acc);
+find_platform_tests([Term | Rest], Acc) when is_tuple(Term) ->
+ find_platform_tests(tuple_to_list(Term) ++ Rest, Acc);
+find_platform_tests([_Term | Rest], Acc) ->
+ find_platform_tests(Rest, Acc).
+
+cache_status() ->
+ Cache = filelib:last_modified("rebar.cache"), %% 0 if not found
+ RebarConfig = filelib:last_modified("rebar.config"),
+ ConfigureErl = filelib:last_modified("configure.erl"),
+ if
+ Cache == 0 -> create;
+ Cache < RebarConfig -> update;
+ Cache < ConfigureErl -> update;
+ true -> ok
+ end.
+
+create_platform_cache(Tests) ->
+ case compile:file("configure", [binary, debug_info, report]) of
+ {ok, _, Bin} ->
+ {module, _} = code:load_binary(configure, "configure", Bin),
+ Vals = [io_lib:format("~w.~n", [{{Test, Args},
+ erlang:apply(configure, Test, Args)}])
+ || {Test, Args} <- Tests],
+ ok = file:write_file("rebar.cache", Vals),
+ true = code:delete(configure),
+ ok;
+ _ ->
+ ?ABORT("Failed to compile platform tests.", [])
+ end.
+
+replace_platform_values(ConfigTerms) ->
+ case file:consult("rebar.cache") of
+ {ok, CacheTerms} ->
+ replace_platform_values(ConfigTerms, CacheTerms, []);
+ _ ->
+ ?ABORT("Failed to read rebar.cache.", [])
+ end.
+
+replace_platform_values([], _, Acc) ->
+ lists:reverse(Acc);
+replace_platform_values([{test, Fun, Args} | Rest], ConfigTerms, Acc) ->
+ replace_platform_values(Rest, ConfigTerms,
+ [proplists:get_value({Fun, Args}, ConfigTerms)
+ | Acc]);
+replace_platform_values([Term | Rest], ConfigTerms, Acc) when is_list(Term) ->
+ List = replace_platform_values(Term, ConfigTerms, []),
+ replace_platform_values(Rest, ConfigTerms,
+ [List | Acc]);
+replace_platform_values([Term | Rest], ConfigTerms, Acc) when is_tuple(Term) ->
+ Tuple = list_to_tuple(
+ replace_platform_values(tuple_to_list(Term), ConfigTerms, [])),
+ replace_platform_values(Rest, ConfigTerms, [Tuple | Acc]);
+replace_platform_values([Term | Rest], ConfigTerms, Acc) ->
+ replace_platform_values(Rest, ConfigTerms, [Term | Acc]).
View
2  src/rebar_erlc_compiler.erl
@@ -152,7 +152,7 @@ doterl_compile(Config, OutDir, MoreSources) ->
%% Make sure that ebin/ exists and is on the path
ok = filelib:ensure_dir(filename:join("ebin", "dummy.beam")),
CurrPath = code:get_path(),
- true = code:add_path("ebin"),
+ true = code:add_path(filename:absname("ebin")),
rebar_base_compiler:run(Config, NewFirstErls, OtherErls,
fun(S, C) ->
internal_erl_compile(S, C, OutDir, ErlOpts)
View
41 src/rebar_eunit.erl
@@ -177,33 +177,40 @@ get_eunit_opts(Config) ->
BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []).
eunit_config(Config) ->
- EqcOpts = case is_quickcheck_avail() of
- true ->
- [{d, 'EQC'}];
- false ->
- []
- end,
+ EqcOpts = eqc_opts(),
+ PropErOpts = proper_opts(),
ErlOpts = rebar_config:get_list(Config, erl_opts, []),
EunitOpts = rebar_config:get_list(Config, eunit_compile_opts, []),
Opts = [{d, 'TEST'}, debug_info] ++
- ErlOpts ++ EunitOpts ++ EqcOpts,
+ ErlOpts ++ EunitOpts ++ EqcOpts ++ PropErOpts,
Config1 = rebar_config:set(Config, erl_opts, Opts),
FirstErls = rebar_config:get_list(Config1, eunit_first_files, []),
rebar_config:set(Config1, erl_first_files, FirstErls).
-is_quickcheck_avail() ->
- case erlang:get(is_quickcheck_avail) of
+
+eqc_opts() ->
+ define_if('PROPER', is_lib_avail(is_eqc_avail, eqc,
+ "eqc.hrl", "QuickCheck")).
+proper_opts() ->
+ define_if('EQC', is_lib_avail(is_proper_avail, proper,
+ "proper.hrl", "PropEr")).
+
+define_if(Def, true) -> [{d, Def}];
+define_if(_Def, false) -> [].
+
+is_lib_avail(DictKey, Mod, Hrl, Name) ->
+ case erlang:get(DictKey) of
undefined ->
- case code:lib_dir(eqc, include) of
- {error, bad_name} ->
- IsAvail = false;
- Dir ->
- IsAvail = filelib:is_regular(filename:join(Dir, "eqc.hrl"))
- end,
- erlang:put(is_quickcheck_avail, IsAvail),
- ?DEBUG("Quickcheck availability: ~p\n", [IsAvail]),
+ 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),
+ ?DEBUG("~s availability: ~p\n", [Name, IsAvail]),
IsAvail;
IsAvail ->
IsAvail
View
7 src/rebar_otp_app.erl
@@ -96,8 +96,13 @@ preprocess(Config, AppSrcFile) ->
AppVars = load_app_vars(Config) ++ [{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),
+ A2 = lists:keystore(vsn, 1, A1, {vsn, Vsn}),
+
%% Build the final spec as a string
- Spec = io_lib:format("~p.\n", [{application, AppName, A1}]),
+ Spec = io_lib:format("~p.\n", [{application, AppName, A2}]),
%% Setup file .app filename and write new contents
AppFile = rebar_app_utils:app_src_to_app(AppSrcFile),
View
48 src/rebar_rel_utils.erl
@@ -26,7 +26,14 @@
%% -------------------------------------------------------------------
-module(rebar_rel_utils).
--export([is_rel_dir/0, is_rel_dir/1]).
+-export([is_rel_dir/0,
+ is_rel_dir/1,
+ get_reltool_release_info/1,
+ get_rel_release_info/1,
+ get_rel_release_info/2,
+ get_previous_release_path/0]).
+
+-include("rebar.hrl").
is_rel_dir() ->
is_rel_dir(rebar_utils:get_cwd()).
@@ -39,3 +46,42 @@ is_rel_dir(Dir) ->
false ->
false
end.
+
+%% Get release name and version from a reltool.config
+get_reltool_release_info(ReltoolFile) ->
+ %% expect sys to be the first proplist in reltool.config
+ case file:consult(ReltoolFile) of
+ {ok, [{sys, Config}| _]} ->
+ %% expect the first rel in the proplist to be the one you want
+ {rel, Name, Ver, _} = proplists:lookup(rel, Config),
+ {Name, Ver};
+ _ ->
+ ?ABORT("Failed to parse ~s~n", [ReltoolFile])
+ end.
+
+%% Get release name and version from a rel file
+get_rel_release_info(RelFile) ->
+ case file:consult(RelFile) of
+ {ok, [{release, {Name, Ver}, _, _}]} ->
+ {Name, Ver};
+ _ ->
+ ?ABORT("Failed to parse ~s~n", [RelFile])
+ end.
+
+%% Get release name and version from a name and a path
+get_rel_release_info(Name, Path) ->
+ [RelFile] = filelib:wildcard(filename:join([Path, "releases", "*",
+ Name ++ ".rel"])),
+ [BinDir|_] = re:replace(RelFile, Name ++ "\\.rel", ""),
+ get_rel_release_info(filename:join([binary_to_list(BinDir),
+ Name ++ ".rel"])).
+
+%% Get the previous release path from a global variable
+get_previous_release_path() ->
+ case rebar_config:get_global(previous_release, false) of
+ false ->
+ ?ABORT("previous_release=PATH is required to "
+ "create upgrade package~n", []);
+ OldVerPath ->
+ OldVerPath
+ end.
View
96 src/rebar_upgrade.erl
@@ -4,7 +4,7 @@
%%
%% rebar: Erlang Build Tools
%%
-%% Copyright (c) 2011 Joe Williams <joe@joetify.com>
+%% Copyright (c) 2011 Joe Williams (joe@joetify.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
@@ -32,91 +32,69 @@
-export(['generate-upgrade'/2]).
-%% public api
+%% ====================================================================
+%% Public API
+%% ====================================================================
'generate-upgrade'(_Config, ReltoolFile) ->
- case rebar_config:get_global(previous_release, false) of
- false ->
- ?ABORT("previous_release=PATH is required to "
- "create upgrade package~n", []);
- OldVerPath ->
- %% Run checks to make sure that building a package is possible
- {NewName, NewVer} = run_checks(OldVerPath, ReltoolFile),
- NameVer = NewName ++ "_" ++ NewVer,
+ %% Get the old release path
+ OldVerPath = rebar_rel_utils:get_previous_release_path(),
- %% Save the code path prior to doing anything
- OrigPath = code:get_path(),
+ %% Run checks to make sure that building a package is possible
+ {NewName, NewVer} = run_checks(OldVerPath, ReltoolFile),
+ NameVer = NewName ++ "_" ++ NewVer,
- %% Prepare the environment for building the package
- ok = setup(OldVerPath, NewName, NewVer, NameVer),
+ %% Save the code path prior to doing anything
+ OrigPath = code:get_path(),
- %% Build the package
- run_systools(NameVer, NewName),
+ %% Prepare the environment for building the package
+ ok = setup(OldVerPath, NewName, NewVer, NameVer),
- %% Boot file changes
- {ok, _} = boot_files(NewVer, NewName),
+ %% Build the package
+ run_systools(NameVer, NewName),
- %% Extract upgrade and tar it back up with changes
- make_tar(NameVer),
+ %% Boot file changes
+ {ok, _} = boot_files(NewVer, NewName),
- %% Clean up files that systools created
- ok = cleanup(NameVer, NewName, NewVer),
+ %% Extract upgrade and tar it back up with changes
+ make_tar(NameVer),
- %% Restore original path
- true = code:set_path(OrigPath),
+ %% Clean up files that systools created
+ ok = cleanup(NameVer, NewName, NewVer),
- ok
- end.
+ %% Restore original path
+ true = code:set_path(OrigPath),
+
+ ok.
-%% internal api
+%% ===================================================================
+%% Internal functions
+%% ==================================================================
run_checks(OldVerPath, ReltoolFile) ->
- true = prop_check(filelib:is_dir(OldVerPath),
+ true = rebar_utils:prop_check(filelib:is_dir(OldVerPath),
"Release directory doesn't exist (~p)~n", [OldVerPath]),
- {Name, Ver} = get_release_name(ReltoolFile),
+ {Name, Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolFile),
NamePath = filename:join([".", Name]),
- true = prop_check(filelib:is_dir(NamePath),
+ true = rebar_utils:prop_check(filelib:is_dir(NamePath),
"Release directory doesn't exist (~p)~n", [NamePath]),
- {NewName, NewVer} = get_release_version(Name, NamePath),
- {OldName, OldVer} = get_release_version(Name, OldVerPath),
+ {NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NamePath),
+ {OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath),
- true = prop_check(NewName == OldName,
+ true = rebar_utils:prop_check(NewName == OldName,
"New and old .rel release names do not match~n", []),
- true = prop_check(Name == NewName,
+ true = rebar_utils:prop_check(Name == NewName,
"Reltool and .rel release names do not match~n", []),
- true = prop_check(NewVer =/= OldVer,
+ true = rebar_utils:prop_check(NewVer =/= OldVer,
"New and old .rel contain the same version~n", []),
- true = prop_check(Ver == NewVer,
+ true = rebar_utils:prop_check(Ver == NewVer,
"Reltool and .rel versions do not match~n", []),
{NewName, NewVer}.
-get_release_name(ReltoolFile) ->
- %% expect sys to be the first proplist in reltool.config
- case file:consult(ReltoolFile) of
- {ok, [{sys, Config}| _]} ->
- %% expect the first rel in the proplist to be the one you want
- {rel, Name, Ver, _} = proplists:lookup(rel, Config),
- {Name, Ver};
- _ ->
- ?ABORT("Failed to parse ~s~n", [ReltoolFile])
- end.
-
-get_release_version(Name, Path) ->
- [RelFile] = filelib:wildcard(filename:join([Path, "releases", "*",
- Name ++ ".rel"])),
- [BinDir|_] = re:replace(RelFile, Name ++ "\\.rel", ""),
- {ok, [{release, {Name1, Ver}, _, _}]} =
- file:consult(filename:join([binary_to_list(BinDir),
- Name ++ ".rel"])),
- {Name1, Ver}.
-
-prop_check(true, _, _) -> true;
-prop_check(false, Msg, Args) -> ?ABORT(Msg, Args).
-
setup(OldVerPath, NewName, NewVer, NameVer) ->
NewRelPath = filename:join([".", NewName]),
Src = filename:join([NewRelPath, "releases",
View
7 src/rebar_utils.erl
@@ -37,7 +37,8 @@
erl_to_mod/1,
abort/2,
escript_foldl/3,
- find_executable/1]).
+ find_executable/1,
+ prop_check/3]).
-include("rebar.hrl").
@@ -151,6 +152,10 @@ find_executable(Name) ->
"\"" ++ filename:nativename(Path) ++ "\""
end.
+%% Helper function for checking values and aborting when needed
+prop_check(true, _, _) -> true;
+prop_check(false, Msg, Args) -> ?ABORT(Msg, Args).
+
%% ====================================================================
%% Internal functions
%% ====================================================================
View
1  test/upgrade_project/README.md
@@ -21,6 +21,7 @@
rebar compile
rebar generate
+ rebar generate-appups previous_release=dummy_0.1
rebar generate-upgrade previous_release=dummy_0.1
tar -zvtf rel/dummy_0.2.tar.gz
View
8 test/upgrade_project/apps/dummy/ebin/dummy.appup
@@ -1,8 +0,0 @@
-{"0.2",
- [{"0.1",[
- {update, dummy_server, {advanced, [foo]}}
- ]}],
- [{"0.1",[
- {update, dummy_server, {advanced, [foo]}}
- ]}]
-}.

No commit comments for this range

Something went wrong with that request. Please try again.