summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Sloughter <t@crashfast.com>2014-07-27 18:36:31 -0500
committerTristan Sloughter <t@crashfast.com>2014-08-16 07:22:27 -0500
commiteb8fa02df7d71435a879de987b3139bb5bffb963 (patch)
treee3ac360353b37811be1a3775d986e4a3ceab16e2
parent19c215ee9fe0726a1983b36f4f8bcc21d42a5ef8 (diff)
large refactoring
Removed separate compilers Resolves apps to build Finds avail deps before pulling/building Includes relx Simplifies build commands
-rw-r--r--.gitignore1
-rw-r--r--Makefile4
-rw-r--r--README.md76
-rwxr-xr-xbootstrap/bootstrap (renamed from bootstrap)2
-rw-r--r--bootstrap/bootstrap.bat (renamed from bootstrap.bat)0
-rwxr-xr-xbootstrap/rebarbin0 -> 160323 bytes
-rw-r--r--ebin/rebar.app86
-rw-r--r--include/rebar.hrl10
-rw-r--r--rebar.config9
-rw-r--r--src/rebar.erl20
-rw-r--r--src/rebar_abnfc_compiler.erl123
-rw-r--r--src/rebar_app_discover.erl84
-rw-r--r--src/rebar_app_info.erl92
-rw-r--r--src/rebar_app_utils.erl5
-rw-r--r--src/rebar_appups.erl219
-rw-r--r--src/rebar_asn1_compiler.erl100
-rw-r--r--src/rebar_config.erl148
-rw-r--r--src/rebar_core.erl629
-rw-r--r--src/rebar_deps.erl855
-rw-r--r--src/rebar_dia_compiler.erl106
-rw-r--r--src/rebar_erlc_compiler.erl40
-rw-r--r--src/rebar_erlydtl_compiler.erl47
-rw-r--r--src/rebar_escripter.erl28
-rw-r--r--src/rebar_fetch.erl247
-rw-r--r--src/rebar_lfe_compiler.erl84
-rw-r--r--src/rebar_metacmds.erl56
-rw-r--r--src/rebar_mustache.erl230
-rw-r--r--src/rebar_neotoma_compiler.erl163
-rw-r--r--src/rebar_otp_app.erl44
-rw-r--r--src/rebar_port_compiler.erl617
-rw-r--r--src/rebar_protobuffs_compiler.erl153
-rw-r--r--src/rebar_provider.erl139
-rw-r--r--src/rebar_prv_app_builder.erl95
-rw-r--r--src/rebar_prv_release.erl37
-rw-r--r--src/rebar_prv_tar.erl37
-rw-r--r--src/rebar_rel_utils.erl246
-rw-r--r--src/rebar_reltool.erl425
-rw-r--r--src/rebar_shell.erl34
-rw-r--r--src/rebar_topo.erl219
-rw-r--r--src/rebar_upgrade.erl266
-rw-r--r--src/rebar_utils.erl7
41 files changed, 1511 insertions, 4272 deletions
diff --git a/.gitignore b/.gitignore
index 15668a7..86ef6aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@
/.eunit
/deps
/.rebar
+rebar.lock
diff --git a/Makefile b/Makefile
index db7d519..7d84d15 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ REBAR=$(PWD)/rebar
RETEST=$(PWD)/deps/retest/retest
all:
- ./bootstrap
+ ./bootstrap/bootstrap
clean:
@rm -rf rebar ebin/*.beam inttest/rt.work rt.work .eunit
@@ -15,7 +15,7 @@ distclean: clean
@rm -rf deps
debug:
- @./bootstrap debug
+ @./bootstrap/bootstrap debug
check: debug xref dialyzer deps test
diff --git a/README.md b/README.md
index 3ec4a3a..e6ccf8f 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,80 @@ configuration work. rebar also provides dependency management, enabling
application writers to easily re-use common libraries from a variety of
locations (git, hg, etc).
+3.0
+====
+
+This is an experimental branch.
+
+### Commands
+
+| Command | Description |
+|----------- |------------ |
+| compile | Build project |
+| shell | Run shell with project apps in path |
+| escriptize | Create escript from project |
+| release | Build release of project |
+| tar | Package release into tarball |
+
+### Missing
+
+* Pre and post hooks
+* Compilers besides erlc
+
+### Changes
+
+* Fetches and builds deps if missing when running any command that relies on them
+* Automatically recognizes `apps` and `libs` directory structure
+* `escriptize` requires `escript_top_level_app` set in `rebar.config`
+* Relx for releases
+
+### Gone
+
+* Reltool integeration
+
+### Providers
+
+Providers are the modules that do the work to fulfill a user's command.
+
+Example:
+
+```erlang
+-module(rebar_prv_something).
+
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, something).
+-define(DEPS, []).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = something,
+ bare = false,
+ deps = ?DEPS,
+ example = "rebar something",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ %% Do something
+ {ok, Config}.
+```
+
+
Building
--------
@@ -37,7 +111,7 @@ https://github.com/rebar/rebar/wiki/rebar
```sh
$ git clone git://github.com/rebar/rebar.git
$ cd rebar
-$ ./bootstrap
+$ ./bootstrap/bootstrap
Recompile: src/getopt
...
Recompile: src/rebar_utils
diff --git a/bootstrap b/bootstrap/bootstrap
index 7d9a1c1..18a243f 100755
--- a/bootstrap
+++ b/bootstrap/bootstrap
@@ -46,6 +46,8 @@ main(Args) ->
false -> undefined
end,
+ os:cmd("./bootstrap/rebar get-deps compile -r"),
+
%% Compile all src/*.erl to ebin
%% To not accidentally try to compile files like Mac OS X resource forks,
%% we only look for rebar source files that start with a letter.
diff --git a/bootstrap.bat b/bootstrap/bootstrap.bat
index b646a7d..b646a7d 100644
--- a/bootstrap.bat
+++ b/bootstrap/bootstrap.bat
diff --git a/bootstrap/rebar b/bootstrap/rebar
new file mode 100755
index 0000000..14e5c22
--- /dev/null
+++ b/bootstrap/rebar
Binary files differ
diff --git a/ebin/rebar.app b/ebin/rebar.app
index 9ee54e6..5c5cc70 100644
--- a/ebin/rebar.app
+++ b/ebin/rebar.app
@@ -3,13 +3,11 @@
{application, rebar,
[{description, "Rebar: Erlang Build Tool"},
- {vsn, "2.5.0"},
+ {vsn, "3.0.0"},
{modules, [ rebar,
- rebar_abnfc_compiler,
rebar_app_utils,
- rebar_appups,
- rebar_asn1_compiler,
- rebar_dia_compiler,
+ rebar_app_info,
+ rebar_app_discover,
rebar_base_compiler,
rebar_cleaner,
rebar_config,
@@ -22,26 +20,23 @@
rebar_erlydtl_compiler,
rebar_escripter,
rebar_eunit,
+ rebar_fetch,
rebar_file_utils,
- rebar_lfe_compiler,
rebar_log,
- rebar_neotoma_compiler,
rebar_otp_app,
- rebar_port_compiler,
- rebar_protobuffs_compiler,
+ rebar_provider,
+ rebar_prv_app_builder,
rebar_qc,
- rebar_rel_utils,
- rebar_reltool,
rebar_require_vsn,
+ rebar_prv_release,
rebar_shell,
rebar_subdirs,
+ rebar_prv_tar,
rebar_templater,
- rebar_upgrade,
+ rebar_topo,
rebar_utils,
rebar_xref,
- rebar_metacmds,
- rebar_getopt,
- rebar_mustache ]},
+ rebar_getopt]},
{registered, []},
{applications, [kernel,
stdlib,
@@ -49,56 +44,19 @@
compiler,
crypto,
syntax_tools,
- tools]},
+ tools,
+ relx]},
{env, [
- %% Default log level
- {log_level, warn},
+ %% Default log level
+ {log_level, warn},
- %% any_dir processing modules
- {any_dir_modules, [
- rebar_require_vsn,
- rebar_deps,
- rebar_subdirs,
- rebar_templater,
- rebar_cleaner
- ]},
-
- %% Dir specific processing modules
- {modules, [
- {app_dir, [
- rebar_abnfc_compiler,
- rebar_protobuffs_compiler,
- rebar_neotoma_compiler,
- rebar_asn1_compiler,
- rebar_dia_compiler,
- rebar_erlc_compiler,
- rebar_lfe_compiler,
- rebar_erlydtl_compiler,
- rebar_port_compiler,
- rebar_otp_app,
- rebar_ct,
- rebar_eunit,
- rebar_qc,
- rebar_escripter,
- rebar_edoc,
- rebar_shell,
- rebar_xref,
- rebar_metacmds
- ]},
-
- {rel_dir, [
- rebar_appups,
- rebar_reltool,
- rebar_upgrade
- ]}
- ]},
- {recursive_cmds, [
- 'check-deps',
- compile,
- 'delete-deps',
- 'get-deps',
- 'list-deps',
- 'update-deps'
- ]}
+ %% any_dir processing modules
+ {providers, [rebar_escripter,
+ rebar_deps,
+ rebar_erlydtl_compiler,
+ rebar_prv_app_builder,
+ rebar_shell,
+ rebar_prv_tar,
+ rebar_prv_release]}
]}
]}.
diff --git a/include/rebar.hrl b/include/rebar.hrl
index b19fdd3..a905569 100644
--- a/include/rebar.hrl
+++ b/include/rebar.hrl
@@ -12,3 +12,13 @@
-define(ERROR(Str, Args), rebar_log:log(standard_error, error, Str, Args)).
-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
+
+-record(provider, {name :: atom(), % The 'user friendly' name of the task
+ provider_impl :: atom(), % The implementation of the task, maybe fun or
+ provides :: atom(),
+ bare :: boolean(), % Indicates whether a build config is needed
+ deps :: [atom()], % The list of dependencies
+ desc :: string(), % The description for the task
+ short_desc :: string(), % A one line short description of the task
+ example :: string(), % An example of the task usage
+ opts :: list()}). % The list of options that the task requires/understands
diff --git a/rebar.config b/rebar.config
index 1c62a55..b959579 100644
--- a/rebar.config
+++ b/rebar.config
@@ -4,6 +4,9 @@
%% escript_incl_extra is for internal rebar-private use only.
%% Do not use outside rebar. Config interface is not stable.
{escript_incl_extra, [{"priv/templates/*", "."}]}.
+{escript_incl_apps,
+ [getopt, erlydtl, erlware_commons, relx]}.
+{escript_top_level_app, rebar}.
%% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17.
%% At the same time, their counterparts dict() and digraph() are to be
@@ -28,3 +31,9 @@
- (\"diameter_dict_util\":\"format_error\"/\"1\")
- (\"diameter_dict_util\":\"parse\"/\"2\"))",
[]}]}.
+
+{first_files, [rebar_provider]}.
+
+{deps, [{relx, "",
+ {git, "https://github.com/erlware/relx.git",
+ {branch, "master"}}}]}.
diff --git a/src/rebar.erl b/src/rebar.erl
index a43da5f..4189b91 100644
--- a/src/rebar.erl
+++ b/src/rebar.erl
@@ -145,10 +145,8 @@ init_config({Options, _NonOptArgs}) ->
BaseConfig = rebar_config:base_config(GlobalConfig2),
- %% 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
- rebar_config:set_xconf(BaseConfig1, vsn_cache, dict:new()).
+ rebar_config:set_xconf(BaseConfig, vsn_cache, dict:new()).
init_config1(BaseConfig) ->
%% Determine the location of the rebar executable; important for pulling
@@ -173,7 +171,10 @@ run_aux(BaseConfig, Commands) ->
BaseConfig1 = init_config1(BaseConfig),
%% Process each command, resetting any state between each one
- rebar_core:process_commands(CommandAtoms, BaseConfig1).
+ {ok, Providers} = application:get_env(rebar, providers),
+ BaseConfig2 = rebar_config:create_logic_providers(Providers, BaseConfig1),
+ rebar_core:process_commands(CommandAtoms, BaseConfig2),
+ ok.
%%
%% print help/usage string
@@ -382,13 +383,6 @@ update-deps Update fetched dependencies
delete-deps Delete fetched dependencies
list-deps List dependencies
-generate [dump_spec=0/1] Build release with reltool
-overlay Run reltool overlays only
-
-generate-upgrade previous_release=path Build an upgrade package
-
-generate-appups previous_release=path Generate appup files
-
eunit [suite[s]=foo] Run EUnit tests in foo.erl and
test/foo_tests.erl
[suite[s]=foo] [test[s]=bar] Run specific EUnit tests [first test
@@ -476,6 +470,7 @@ command_names() ->
"check-deps",
"clean",
"compile",
+ "release",
"create",
"create-app",
"create-lib",
@@ -485,9 +480,6 @@ command_names() ->
"doc",
"eunit",
"escriptize",
- "generate",
- "generate-appups",
- "generate-upgrade",
"get-deps",
"help",
"list-deps",
diff --git a/src/rebar_abnfc_compiler.erl b/src/rebar_abnfc_compiler.erl
deleted file mode 100644
index 37731b5..0000000
--- a/src/rebar_abnfc_compiler.erl
+++ /dev/null
@@ -1,123 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2010 Anthony Ramine (nox@dev-extend.eu),
-%%
-%% Permission is hereby granted, free of charge, to any person obtaining a copy
-%% of this software and associated documentation files (the "Software"), to deal
-%% in the Software without restriction, including without limitation the rights
-%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-%% copies of the Software, and to permit persons to whom the Software is
-%% furnished to do so, subject to the following conditions:
-%%
-%% The above copyright notice and this permission notice shall be included in
-%% all copies or substantial portions of the Software.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-%% THE SOFTWARE.
-%% -------------------------------------------------------------------
-
-%% The rebar_abnfc_compiler module is a plugin for rebar that compiles
-%% ABNF grammars into parsers. By default, it compiles all src/*.abnf
-%% to src/*.erl.
-%%
-%% Configuration options should be placed in rebar.config under
-%% 'abnfc_opts'. Available options include:
-%%
-%% doc_root: where to find the ABNF grammars to compile
-%% "src" by default
-%%
-%% out_dir: where to put the generated files.
-%% "src" by default
-%%
-%% source_ext: the file extension the ABNF grammars have.
-%% ".abnf" by default
-%%
-%% module_ext: characters to append to the parser's module name
-%% "" by default
--module(rebar_abnfc_compiler).
-
--export([compile/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
-compile(Config, _AppFile) ->
- DtlOpts = abnfc_opts(Config),
- rebar_base_compiler:run(Config, [],
- option(doc_root, DtlOpts),
- option(source_ext, DtlOpts),
- option(out_dir, DtlOpts),
- option(module_ext, DtlOpts) ++ ".erl",
- fun compile_abnfc/3).
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, compile) ->
- ?CONSOLE(
- "Build ABNF (*.abnf) sources.~n"
- "~n"
- "Valid rebar.config options:~n"
- " ~p~n",
- [
- {abnfc_opts, [{doc_root, "src"},
- {out_dir, "src"},
- {source_ext, ".abnfc"},
- {module_ext, ""}]}
- ]).
-
-abnfc_opts(Config) ->
- rebar_config:get(Config, abnfc_opts, []).
-
-option(Opt, DtlOpts) ->
- proplists:get_value(Opt, DtlOpts, default(Opt)).
-
-default(doc_root) -> "src";
-default(out_dir) -> "src";
-default(source_ext) -> ".abnf";
-default(module_ext) -> "".
-
-abnfc_is_present() ->
- code:which(abnfc) =/= non_existing.
-
-compile_abnfc(Source, _Target, Config) ->
- case abnfc_is_present() of
- false ->
- ?ERROR("~n===============================================~n"
- " You need to install abnfc to compile ABNF grammars~n"
- " Download the latest tarball release from github~n"
- " https://github.com/nygge/abnfc~n"
- " and install it into your erlang library dir~n"
- "===============================================~n~n", []),
- ?FAIL;
- true ->
- AbnfcOpts = abnfc_opts(Config),
- SourceExt = option(source_ext, AbnfcOpts),
- Opts = [noobj,
- {o, option(out_dir, AbnfcOpts)},
- {mod, filename:basename(Source, SourceExt) ++
- option(module_ext, AbnfcOpts)}],
- case abnfc:file(Source, Opts) of
- ok -> ok;
- Error ->
- ?ERROR("Compiling grammar ~s failed:~n ~p~n",
- [Source, Error]),
- ?FAIL
- end
- end.
diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl
new file mode 100644
index 0000000..4252262
--- /dev/null
+++ b/src/rebar_app_discover.erl
@@ -0,0 +1,84 @@
+-module(rebar_app_discover).
+
+-export([do/2,
+ find_apps/1]).
+
+do(Config, LibDirs) ->
+ Apps = find_apps(LibDirs),
+ lists:foldl(fun(AppInfo, ConfigAcc) ->
+ rebar_config:add_app(ConfigAcc, AppInfo)
+ end, Config, Apps).
+
+-spec all_app_dirs(list(file:name())) -> list(file:name()).
+all_app_dirs(LibDirs) ->
+ lists:flatmap(fun(LibDir) ->
+ app_dirs(LibDir)
+ end, LibDirs).
+
+app_dirs(LibDir) ->
+ Path1 = filename:join([LibDir,
+ "*",
+ "src",
+ "*.app.src"]),
+ Path2 = filename:join([LibDir,
+ "src",
+ "*.app.src"]),
+
+ Path3 = filename:join([LibDir,
+ "*",
+ "ebin",
+ "*.app"]),
+ Path4 = filename:join([LibDir,
+ "ebin",
+ "*.app"]),
+
+ lists:usort(lists:foldl(fun(Path, Acc) ->
+ Files = filelib:wildcard(Path),
+ [app_dir(File) || File <- Files] ++ Acc
+ end, [], [Path1, Path2, Path3, Path4])).
+
+find_apps(LibDirs) ->
+ lists:map(fun(AppDir) ->
+ AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])),
+ AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])),
+ case AppFile of
+ [File] ->
+ AppInfo = create_app_info(AppDir, File),
+ AppInfo1 = rebar_app_info:app_file(AppInfo, File),
+ case AppSrcFile of
+ [F] ->
+ rebar_app_info:app_file_src(AppInfo1, F);
+ [] ->
+ AppInfo1
+ end;
+ [] ->
+ case AppSrcFile of
+ [File] ->
+ AppInfo = create_app_info(AppDir, File),
+ rebar_app_info:app_file_src(AppInfo, File);
+ [] ->
+ error
+ end
+ end
+ end, all_app_dirs(LibDirs)).
+
+app_dir(AppFile) ->
+ filename:join(lists:droplast(filename:split(filename:dirname(AppFile)))).
+
+create_app_info(AppDir, AppFile) ->
+ case file:consult(AppFile) of
+ {ok, [{application, AppName, AppDetails}]} ->
+ AppVsn = proplists:get_value(vsn, AppDetails),
+ AbsCwd = filename:absname(rebar_utils:get_cwd()),
+ {ok, AppInfo} = rebar_app_info:new(AppName, AppVsn, AppDir),
+ RebarConfig = filename:join(AppDir, "rebar.config"),
+ AppConfig = case filelib:is_file(RebarConfig) of
+ true ->
+ rebar_config:new(RebarConfig);
+ false ->
+ rebar_config:new()
+ end,
+ AppConfig1 = rebar_config:set_xconf(AppConfig, base_dir, AbsCwd),
+ AppInfo1 = rebar_app_info:config(AppInfo, AppConfig1),
+ rebar_app_info:dir(AppInfo1, AppDir)
+ end.
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
new file mode 100644
index 0000000..503d3bc
--- /dev/null
+++ b/src/rebar_app_info.erl
@@ -0,0 +1,92 @@
+-module(rebar_app_info).
+
+-export([new/0,
+ new/3,
+ name/1,
+ name/2,
+ config/1,
+ config/2,
+ app_file_src/1,
+ app_file_src/2,
+ app_file/1,
+ app_file/2,
+ original_vsn/1,
+ dir/1,
+ dir/2]).
+
+-export_type([t/0]).
+
+-record(app_info_t, {name :: atom(),
+ app_file_src :: file:name() | undefined,
+ app_file :: file:name(),
+ config :: rebar_config:config() | undefined,
+ original_vsn :: string(),
+ dir :: file:name(),
+ source :: string() | undefined}).
+
+%%============================================================================
+%% types
+%%============================================================================
+-opaque t() :: record(app_info_t).
+
+%%============================================================================
+%% API
+%% ============================================================================
+%% @doc Build a new, empty, app info value. This is not of a lot of use and you
+%% probably wont be doing this much.
+-spec new() -> {ok, t()}.
+new() ->
+ {ok, #app_info_t{}}.
+
+%% @doc build a complete version of the app info with all fields set.
+-spec new(atom(), string(), file:name()) ->
+ {ok, t()}.
+new(AppName, Vsn, Dir)
+ when erlang:is_atom(AppName) ->
+ {ok, #app_info_t{name=AppName,
+ original_vsn=Vsn,
+ dir=Dir}}.
+
+-spec name(t()) -> atom().
+name(#app_info_t{name=Name}) ->
+ Name.
+
+-spec name(t(), atom()) -> t().
+name(AppInfo=#app_info_t{}, AppName)
+ when erlang:is_atom(AppName) ->
+ AppInfo#app_info_t{name=AppName}.
+
+-spec config(t()) -> rebar_config:confg().
+config(#app_info_t{config=Config}) ->
+ Config.
+
+-spec config(t(), rebar_config:confg()) -> t().
+config(AppInfo=#app_info_t{}, Config) ->
+ AppInfo#app_info_t{config=Config}.
+
+-spec app_file_src(t()) -> file:name().
+app_file_src(#app_info_t{app_file_src=AppFileSrc}) ->
+ AppFileSrc.
+
+-spec app_file_src(t(), file:name()) -> t().
+app_file_src(AppInfo=#app_info_t{}, AppFileSrc) ->
+ AppInfo#app_info_t{app_file_src=AppFileSrc}.
+
+-spec app_file(t()) -> file:name().
+app_file(#app_info_t{app_file=AppFile}) ->
+ AppFile.
+
+-spec app_file(t(), file:name()) -> t().
+app_file(AppInfo=#app_info_t{}, AppFile) ->
+ AppInfo#app_info_t{app_file=AppFile}.
+
+-spec original_vsn(t()) -> string().
+original_vsn(#app_info_t{original_vsn=Vsn}) ->
+ Vsn.
+
+-spec dir(t()) -> file:name().
+dir(#app_info_t{dir=Dir}) ->
+ Dir.
+-spec dir(t(), file:name()) -> t().
+dir(AppInfo=#app_info_t{}, Dir) ->
+ AppInfo#app_info_t{dir=Dir}.
diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl
index a2484e1..bbcbaf4 100644
--- a/src/rebar_app_utils.erl
+++ b/src/rebar_app_utils.erl
@@ -75,7 +75,10 @@ is_app_src(Filename) ->
Filename =/= filename:rootname(Filename, ".app.src").
app_src_to_app(Filename) ->
- filename:join("ebin", filename:basename(Filename, ".app.src") ++ ".app").
+ Path = filename:join(lists:droplast(filename:split(filename:dirname(Filename)))),
+ AppFile = filename:join([Path, "ebin", filename:basename(Filename, ".app.src") ++ ".app"]),
+ filelib:ensure_dir(AppFile),
+ AppFile.
app_name(Config, AppFile) ->
case load_app_file(Config, AppFile) of
diff --git a/src/rebar_appups.erl b/src/rebar_appups.erl
deleted file mode 100644
index 38e7b72..0000000
--- a/src/rebar_appups.erl
+++ /dev/null
@@ -1,219 +0,0 @@
-%% -*- 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]).
-
-%% for internal use only
--export([info/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
- {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
- TargetParentDir = rebar_rel_utils:get_target_parent_dir(Config,
- ReltoolConfig),
-
- PrevRelPath = rebar_rel_utils:get_previous_release_path(Config),
- OldVerPath = filename:join([TargetParentDir, PrevRelPath]),
-
- ModDeps = rebar_config:get(Config, module_deps, []),
-
- %% Get the new and old release name and versions
- {Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
- NewVerPath = filename:join([TargetParentDir, 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", []),
-
- %% Find all the apps that have been upgraded
- {_Added, _Removed, Upgraded} = get_apps(Name, OldVerPath, NewVerPath),
-
- %% Get a list of any appup files that exist in the new release
- NewAppUpFiles = rebar_utils:find_files(
- filename:join([NewVerPath, "lib"]), "^[^._].*.appup$"),
-
- %% Convert the list of appup files into app names
- AppUpApps = [file_to_name(File) || File <- NewAppUpFiles],
-
- %% Create a list of apps that don't already have appups
- UpgradeApps = genappup_which_apps(Upgraded, AppUpApps),
-
- %% Generate appup files for upgraded apps
- generate_appup_files(NewVerPath, OldVerPath, ModDeps, UpgradeApps),
-
- {ok, Config1}.
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, 'generate-appups') ->
- ?CONSOLE("Generate appup files.~n"
- "~n"
- "Valid command line options:~n"
- " previous_release=path~n",
- []).
-
-get_apps(Name, OldVerPath, NewVerPath) ->
- OldApps = rebar_rel_utils:get_rel_apps(Name, OldVerPath),
- ?DEBUG("Old Version Apps: ~p~n", [OldApps]),
-
- NewApps = rebar_rel_utils:get_rel_apps(Name, NewVerPath),
- ?DEBUG("New Version Apps: ~p~n", [NewApps]),
-
- Added = app_list_diff(NewApps, OldApps),
- ?DEBUG("Added: ~p~n", [Added]),
-
- Removed = app_list_diff(OldApps, NewApps),
- ?DEBUG("Removed: ~p~n", [Removed]),
-
- PossiblyUpgraded = proplists:get_keys(NewApps),
-
- UpgradedApps = [upgraded_app(AppName,
- proplists:get_value(AppName, OldApps),
- proplists:get_value(AppName, NewApps))
- || AppName <- PossiblyUpgraded],
-
- Upgraded = lists:dropwhile(fun(Elem) ->
- Elem == false
- end, lists:sort(UpgradedApps)),
-
- ?DEBUG("Upgraded: ~p~n", [Upgraded]),
-
- {Added, Removed, Upgraded}.
-
-upgraded_app(AppName, OldAppVer, NewAppVer) when OldAppVer /= NewAppVer ->
- {AppName, {OldAppVer, NewAppVer}};
-upgraded_app(_, _, _) ->
- false.
-
-app_list_diff(List1, List2) ->
- List3 = lists:umerge(lists:sort(proplists:get_keys(List1)),
- lists:sort(proplists:get_keys(List2))),
- List3 -- proplists:get_keys(List2).
-
-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(NewVerPath, OldVerPath, ModDeps, [{_App, {undefined, _}}|Rest]) ->
- generate_appup_files(NewVerPath, OldVerPath, ModDeps, Rest);
-generate_appup_files(NewVerPath, OldVerPath, ModDeps, [{App, {OldVer, NewVer}}|Rest]) ->
- OldEbinDir = filename:join([OldVerPath, "lib",
- atom_to_list(App) ++ "-" ++ OldVer, "ebin"]),
- NewEbinDir = filename:join([NewVerPath, "lib",
- atom_to_list(App) ++ "-" ++ NewVer, "ebin"]),
-
- {AddedFiles, DeletedFiles, ChangedFiles} = beam_lib:cmp_dirs(NewEbinDir,
- OldEbinDir),
-
- ChangedNames = [list_to_atom(file_to_name(F)) || {F, _} <- ChangedFiles],
- ModDeps1 = [{N, [M1 || M1 <- M, lists:member(M1, ChangedNames)]}
- || {N, M} <- ModDeps],
-
- Added = [generate_instruction(added, File) || File <- AddedFiles],
- Deleted = [generate_instruction(deleted, File) || File <- DeletedFiles],
- Changed = [generate_instruction(changed, ModDeps1, 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(NewVerPath, OldVerPath, ModDeps, 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, ModDeps, {File, _}) ->
- {ok, {Name, List}} = beam_lib:chunks(File, [attributes, exports]),
- Behavior = get_behavior(List),
- CodeChange = is_code_change(List),
- Deps = proplists:get_value(Name, ModDeps, []),
- generate_instruction_advanced(Name, Behavior, CodeChange, Deps).
-
-generate_instruction_advanced(Name, undefined, undefined, Deps) ->
- %% Not a behavior or code change, assume purely functional
- {load_module, Name, Deps};
-generate_instruction_advanced(Name, [supervisor], _, _) ->
- %% Supervisor
- {update, Name, supervisor};
-generate_instruction_advanced(Name, _, code_change, Deps) ->
- %% Includes code_change export
- {update, Name, {advanced, []}, Deps};
-generate_instruction_advanced(Name, _, _, Deps) ->
- %% Anything else
- {load_module, Name, Deps}.
-
-get_behavior(List) ->
- Attributes = proplists:get_value(attributes, List),
- case proplists:get_value(behavior, Attributes) of
- undefined -> proplists:get_value(behaviour, Attributes);
- Else -> Else
- end.
-
-is_code_change(List) ->
- Exports = proplists:get_value(exports, List),
- case proplists:is_defined(code_change, Exports) of
- true ->
- code_change;
- false ->
- undefined
- end.
diff --git a/src/rebar_asn1_compiler.erl b/src/rebar_asn1_compiler.erl
deleted file mode 100644
index 25e3fd3..0000000
--- a/src/rebar_asn1_compiler.erl
+++ /dev/null
@@ -1,100 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.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_asn1_compiler).
--author('ruslan@babayev.com').
-
--export([compile/2,
- clean/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
--spec compile(rebar_config:config(), file:filename()) -> 'ok'.
-compile(Config, _AppFile) ->
- rebar_base_compiler:run(Config, filelib:wildcard("asn1/*.asn1"),
- "asn1", ".asn1", "src", ".erl",
- fun compile_asn1/3).
-
--spec clean(rebar_config:config(), file:filename()) -> 'ok'.
-clean(_Config, _AppFile) ->
- GeneratedFiles = asn_generated_files("asn1", "src", "include"),
- ok = rebar_file_utils:delete_each(GeneratedFiles),
- ok.
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, compile) ->
- info_help("Build ASN.1 (*.asn1) sources");
-info(help, clean) ->
- info_help("Delete ASN.1 (*.asn1) results").
-
-info_help(Description) ->
- ?CONSOLE(
- "~s.~n"
- "~n"
- "Valid rebar.config options:~n"
- " {asn1_opts, []} (see asn1ct:compile/2 documentation)~n",
- [Description]).
-
--spec compile_asn1(file:filename(), file:filename(),
- rebar_config:config()) -> ok.
-compile_asn1(Source, Target, Config) ->
- ok = filelib:ensure_dir(Target),
- ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")),
- Opts = [{outdir, "src"}, noobj] ++ rebar_config:get(Config, asn1_opts, []),
- case asn1ct:compile(Source, Opts) of
- ok ->
- Asn1 = filename:basename(Source, ".asn1"),
- HrlFile = filename:join("src", Asn1 ++ ".hrl"),
- case filelib:is_regular(HrlFile) of
- true ->
- ok = rebar_file_utils:mv(HrlFile, "include");
- false ->
- ok
- end;
- {error, _Reason} ->
- ?FAIL
- end.
-
-asn_generated_files(AsnDir, SrcDir, IncDir) ->
- lists:foldl(
- fun(AsnFile, Acc) ->
- Base = filename:rootname(filename:basename(AsnFile)),
- [filename:join([IncDir, Base ++ ".hrl"])|
- filelib:wildcard(filename:join([SrcDir, Base ++ ".*"]))] ++ Acc
- end,
- [],
- filelib:wildcard(filename:join([AsnDir, "*.asn1"]))
- ).
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index bdc3fb5..11756b7 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -26,7 +26,7 @@
%% -------------------------------------------------------------------
-module(rebar_config).
--export([new/0, new/1, base_config/1, consult_file/1,
+-export([new/0, new/1, new2/2, base_config/1, consult_file/1,
get/3, get_local/3, get_list/3,
get_all/2,
set/3,
@@ -34,7 +34,11 @@
is_recursive/1,
save_env/3, get_env/2, reset_envs/1,
set_skip_dir/2, is_skip_dir/2, reset_skip_dirs/1,
- clean_config/2,
+ create_logic_providers/2,
+ providers/1, providers/2, add_provider/2,
+ add_dep/2, get_dep/2, deps/2, deps/1, deps_graph/1, deps_graph/2, deps_to_build/1,
+ goals/1, goals/2,
+ get_app/2, apps_to_build/1, apps_to_build/2, add_app/2, replace_app/3,
set_xconf/3, get_xconf/2, get_xconf/3, erase_xconf/2]).
-include("rebar.hrl").
@@ -49,9 +53,16 @@
-record(config, { dir :: file:filename(),
opts = [] :: list(),
+ local_opts = [] :: list(),
globals = new_globals() :: rebar_dict(),
envs = new_env() :: rebar_dict(),
%% cross-directory/-command config
+ goals = [],
+ providers = [],
+ apps_to_build = [],
+ deps_to_build = [],
+ deps = [],
+ deps_graph = undefined,
skip_dirs = new_skip_dirs() :: rebar_dict(),
xconf = new_xconf() :: rebar_dict() }).
@@ -60,6 +71,7 @@
-opaque config() :: #config{}.
-define(DEFAULT_NAME, "rebar.config").
+-define(LOCK_FILE, "rebar.lock").
%% ===================================================================
%% Public API
@@ -80,11 +92,44 @@ new(ConfigFile) when is_list(ConfigFile) ->
Other ->
?ABORT("Failed to load ~s: ~p~n", [ConfigFile, Other])
end;
-new(_ParentConfig=#config{opts=Opts0, globals=Globals, skip_dirs=SkipDirs,
- xconf=Xconf}) ->
+new(_ParentConfig=#config{opts=Opts0, globals=Globals, skip_dirs=SkipDirs, xconf=Xconf}) ->
new(#config{opts=Opts0, globals=Globals, skip_dirs=SkipDirs, xconf=Xconf},
?DEFAULT_NAME).
+new(ParentConfig, ConfName) ->
+ %% Load terms from rebar.config, if it exists
+ Dir = rebar_utils:get_cwd(),
+ new(ParentConfig, ConfName, Dir).
+
+new2(_ParentConfig=#config{opts=Opts0, globals=Globals, skip_dirs=SkipDirs, xconf=Xconf}, Dir) ->
+ new(#config{opts=Opts0, globals=Globals, skip_dirs=SkipDirs, xconf=Xconf},
+ ?DEFAULT_NAME, Dir).
+
+new(ParentConfig, ConfName, Dir) ->
+ ConfigFile = filename:join([Dir, ConfName]),
+ Opts0 = ParentConfig#config.opts,
+ Opts = case consult_file(ConfigFile) of
+ {ok, Terms} ->
+ Terms;
+ {error, enoent} ->
+ [];
+ Other ->
+ ?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other])
+ end,
+
+ Opts1 = case consult_file(?LOCK_FILE) of
+ {ok, [D]} ->
+ lists:keyreplace(deps, 1, Opts, {deps, D});
+ _ ->
+ Opts
+ end,
+
+ ProviderModules = [],
+ create_logic_providers(ProviderModules, ParentConfig#config{dir=Dir
+ ,local_opts=Opts1
+ ,opts=Opts0}).
+
+
get(Config, Key, Default) ->
proplists:get_value(Key, Config#config.opts, Default).
@@ -92,7 +137,7 @@ get_list(Config, Key, Default) ->
get(Config, Key, Default).
get_local(Config, Key, Default) ->
- proplists:get_value(Key, local_opts(Config#config.opts, []), Default).
+ proplists:get_value(Key, Config#config.local_opts, Default).
get_all(Config, Key) ->
proplists:get_all_values(Key, Config#config.opts).
@@ -121,6 +166,8 @@ get_global(Config, Key, Default) ->
is_recursive(Config) ->
get_xconf(Config, recursive, false).
+consult_file(File) when is_binary(File) ->
+ consult_file(binary_to_list(File));
consult_file(File) ->
case filename:extension(File) of
".script" ->
@@ -183,38 +230,68 @@ erase_xconf(Config, Key) ->
NewXconf = dict:erase(Key, Config#config.xconf),
Config#config{xconf = NewXconf}.
-%% TODO: reconsider after config inheritance removal/redesign
-clean_config(Old, New) ->
- New#config{opts=Old#config.opts}.
+get_dep(#config{deps=Apps}, Name) ->
+ lists:keyfind(Name, 2, Apps).
+
+deps(#config{deps=Apps}) ->
+ Apps.
+
+deps(Config, Apps) ->
+ Config#config{deps=Apps}.
+
+deps_graph(#config{deps_graph=Graph}) ->
+ Graph.
+
+deps_graph(Config, Graph) ->
+ Config#config{deps_graph=Graph}.
+
+get_app(#config{apps_to_build=Apps}, Name) ->
+ lists:keyfind(Name, 2, Apps).
+
+apps_to_build(#config{apps_to_build=Apps}) ->
+ Apps.
+
+apps_to_build(Config, Apps) ->
+ Config#config{apps_to_build=Apps}.
+
+add_app(Config=#config{apps_to_build=Apps}, App) ->
+ Config#config{apps_to_build=[App | Apps]}.
+
+replace_app(Config=#config{apps_to_build=Apps}, Name, App) ->
+ Apps1 = lists:keydelete(Name, 2, Apps),
+ Config#config{apps_to_build=[App | Apps1]}.
+
+deps_to_build(#config{deps_to_build=Apps}) ->
+ Apps.
+
+add_dep(Config=#config{deps_to_build=Apps}, App) ->
+ Config#config{deps_to_build=[App | Apps]}.
+
+providers(#config{providers=Providers}) ->
+ Providers.
+
+providers(Config, NewProviders) ->
+ Config#config{providers=NewProviders}.
+
+goals(#config{goals=Goals}) ->
+ Goals.
+
+goals(Config, Goals) ->
+ Config#config{goals=Goals}.
+
+add_provider(Config=#config{providers=Providers}, Provider) ->
+ Config#config{providers=[Provider | Providers]}.
+
+create_logic_providers(ProviderModules, State0) ->
+ lists:foldl(fun(ProviderMod, Acc) ->
+ {ok, State1} = rebar_provider:new(ProviderMod, Acc),
+ State1
+ end, State0, ProviderModules).
%% ===================================================================
%% 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),
@@ -240,13 +317,6 @@ bs(Vars) ->
erl_eval:add_binding(K, V, Bs)
end, erl_eval:new_bindings(), Vars).
-local_opts([], Acc) ->
- lists:reverse(Acc);
-local_opts([local | _Rest], Acc) ->
- lists:reverse(Acc);
-local_opts([Item | Rest], Acc) ->
- local_opts(Rest, [Item | Acc]).
-
new_globals() -> dict:new().
new_env() -> dict:new().
diff --git a/src/rebar_core.erl b/src/rebar_core.erl
index 4557bb8..55a96e9 100644
--- a/src/rebar_core.erl
+++ b/src/rebar_core.erl
@@ -35,431 +35,47 @@
%% ===================================================================
help(ParentConfig, Commands) ->
- %% get all core modules
- {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),
- {ok, RawCoreModules} = application:get_env(rebar, modules),
- AppDirModules = proplists:get_value(app_dir, RawCoreModules),
- RelDirModules = proplists:get_value(rel_dir, RawCoreModules),
- CoreModules = AnyDirModules ++ AppDirModules ++ RelDirModules,
+ {ok, AllProviders} = application:get_env(rebar, providers),
- %% get plugin modules
- Predirs = [],
+ %% get plugin providers
Dir = rebar_utils:get_cwd(),
- PredirsAssoc = remember_cwd_predirs(Dir, Predirs),
- Config = maybe_load_local_config(Dir, ParentConfig),
- {ok, PluginModules} = plugin_modules(Config, PredirsAssoc),
-
- AllModules = CoreModules ++ PluginModules,
+ _Config = maybe_load_local_config(Dir, ParentConfig),
lists:foreach(
fun(Cmd) ->
?CONSOLE("==> help ~p~n~n", [Cmd]),
- CmdModules = select_modules(AllModules, Cmd, []),
- Modules = select_modules(CmdModules, info, []),
+ CmdProviders = rebar_provider:get_target_provider(Cmd, AllProviders),
+ Providers = rebar_provider:get_target_provider(info, CmdProviders),
lists:foreach(fun(M) ->
?CONSOLE("=== ~p:~p ===~n", [M, Cmd]),
M:info(help, Cmd),
?CONSOLE("~n", [])
- end, Modules)
+ end, Providers)
end, Commands).
-process_commands([], ParentConfig) ->
- AbortTrapped = rebar_config:get_xconf(ParentConfig, abort_trapped, false),
- case {get_operations(ParentConfig), AbortTrapped} of
- {0, _} ->
- %% None of the commands had any effect
- ?FAIL;
- {_, true} ->
- %% An abort was previously trapped
- ?FAIL;
- _ ->
- ok
- end;
-process_commands([Command | Rest], ParentConfig) ->
- %% Reset skip dirs
- ParentConfig1 = rebar_config:reset_skip_dirs(ParentConfig),
- Operations = get_operations(ParentConfig1),
-
- 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(),
- Command, ParentConfig1,
- 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,
- %% TODO: reconsider after config inheritance removal/re-design
- 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_xconf(ParentConfig1, keep_going, false) of
- false ->
- ?FAIL;
- true ->
- ?WARN("Continuing on after abort: ~p\n", [Rest]),
- rebar_config:set_xconf(ParentConfig1,
- abort_trapped, true)
- end
- end,
- process_commands(Rest, ParentConfig4).
-
-process_dir(Dir, Command, ParentConfig, DirSet) ->
- case filelib:is_dir(Dir) of
- false ->
- ?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]),
- {ParentConfig, DirSet};
- true ->
- WouldCd = would_cd_into_dir(Dir, Command, ParentConfig),
- ok = file:set_cwd(Dir),
- Config = maybe_load_local_config(Dir, ParentConfig),
-
- %% Save the current code path and then update it with
- %% lib_dirs. Children inherit parents code path, but we also
- %% want to ensure that we restore everything to pristine
- %% condition after processing this child
- CurrentCodePath = update_code_path(Config),
-
- %% Get the list of processing modules and check each one
- %% against CWD to see if it's a fit -- if it is, use that
- %% set of modules to process this dir.
- {ok, AvailModuleSets} = application:get_env(rebar, modules),
- ModuleSet = choose_module_set(AvailModuleSets, Dir),
- skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
- ModuleSet, WouldCd)
- end.
-
-would_cd_into_dir(Dir, Command, Config) ->
- case would_cd_into_dir1(Dir, Command, Config) of
- true ->
- would_cd;
- false ->
- would_not_cd
- end.
-
-would_cd_into_dir1(Dir, Command, Config) ->
- rebar_utils:processing_base_dir(Config, Dir) orelse
- rebar_config:is_recursive(Config) orelse
- is_recursive_command(Command, Config) orelse
- is_generate_in_rel_dir(Command, Dir).
-
-%% Check whether the command is part of the built-in (or extended via
-%% rebar.config) list of default-recursive commands.
-is_recursive_command(Command, Config) ->
- {ok, AppCmds} = application:get_env(rebar, recursive_cmds),
- ConfCmds = rebar_config:get_local(Config, recursive_cmds, []),
- RecursiveCmds = AppCmds ++ ConfCmds,
- lists:member(Command, RecursiveCmds).
-
-%% If the directory we're about to process contains
-%% reltool.config[.script] and the command to be applied is
-%% 'generate', then it's safe to process. We do this to retain the
-%% behavior of specifying {sub_dirs, ["rel"]} and have "rebar generate"
-%% pick up rel/reltool.config[.script]. Without this workaround you'd
-%% have to run "rebar -r generate" (which you don't want to do if you
-%% have deps or other sub_dirs) or "cd rel && rebar generate".
-is_generate_in_rel_dir(generate, Dir) ->
- case rebar_rel_utils:is_rel_dir(Dir) of
- {true, _} ->
- true;
- false ->
- false
- end;
-is_generate_in_rel_dir(_, _) ->
- false.
-
-skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
- {[], undefined}=ModuleSet, WouldCd) ->
- process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet,
- WouldCd);
-skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
- {_, File}=ModuleSet, WouldCd) ->
- case lists:suffix(".app.src", File)
- orelse lists:suffix(".app", File) of
- true ->
- %% .app or .app.src file, check if is_skipped_app
- skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
- ModuleSet, WouldCd, File);
- false ->
- %% not an app dir, no need to consider apps=/skip_apps=
- process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
- ModuleSet, WouldCd)
- end.
-
-skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet,
- WouldCd, AppFile) ->
- case rebar_app_utils:is_skipped_app(Config, AppFile) of
- {Config1, {true, _SkippedApp}} when Command == 'update-deps' ->
- %% update-deps does its own app skipping. Unfortunately there's no
- %% way to signal this to rebar_core, so we have to explicitly do it
- %% here... Otherwise if you use app=, it'll skip the toplevel
- %% directory and nothing will be updated.
- process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath,
- ModuleSet, WouldCd);
- {Config1, {true, SkippedApp}} ->
- ?DEBUG("Skipping app: ~p~n", [SkippedApp]),
- {increment_operations(Config1), DirSet};
- {Config1, false} ->
- process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath,
- ModuleSet, WouldCd)
- end.
-
-process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
- {DirModules, File}, WouldCd) ->
- Config0 = rebar_config:set_xconf(Config, current_command, Command),
-
- %% Get the list of modules for "any dir". This is a catch-all list
- %% of modules that are processed in addition to modules associated
- %% with this directory type. These any_dir modules are processed
- %% FIRST.
- {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),
-
- Modules = AnyDirModules ++ DirModules,
-
- %% Invoke 'preprocess' on the modules -- this yields a list of other
- %% directories that should be processed _before_ the current one.
- {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, File),
-
- %% Remember associated pre-dirs (used for plugin lookup)
- PredirsAssoc = remember_cwd_predirs(Dir, Predirs),
-
- %% Get the list of plug-in modules from rebar.config. These
- %% modules may participate in preprocess and postprocess.
- {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc),
- AllModules = Modules ++ PluginModules,
-
- {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, Config1,
- File),
-
- AllPredirs = Predirs ++ PluginPredirs,
-
- ?DEBUG("Predirs: ~p\n", [AllPredirs]),
- {Config3, DirSet2} = process_each(AllPredirs, Command, Config2, DirSet,
- File),
-
- %% Make sure the CWD is reset properly; processing the dirs may have
- %% caused it to change
- ok = file:set_cwd(Dir),
-
- %% Maybe apply command to Dir
- Config4 = maybe_execute(Dir, Command, Config3, Modules, PluginModules,
- AllModules, File, WouldCd),
-
- %% 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.
- {Config5, Postdirs} = acc_modules(AllModules, postprocess, Config4, File),
- ?DEBUG("Postdirs: ~p\n", [Postdirs]),
- Res = process_each(Postdirs, Command, Config5, DirSet3, File),
-
- %% Make sure the CWD is reset properly; processing the dirs may have
- %% caused it to change
- ok = file:set_cwd(Dir),
-
- %% Once we're all done processing, reset the code path to whatever
- %% the parent initialized it to
- restore_code_path(CurrentCodePath),
-
- %% Return the updated {config, dirset} as result
- Res.
-
-maybe_execute(Dir, Command, Config, Modules, PluginModules, AllModules, File,
- would_cd) ->
- %% Check that this directory is not on the skip list
- case rebar_config:is_skip_dir(Config, 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]),
- Config;
-
- false ->
- %% Check for and get command specific environments
- {Config1, Env} = setup_envs(Config, Modules),
-
- %% Execute any before_command plugins on this directory
- Config2 = execute_pre(Command, PluginModules, Config1, File, Env),
-
- %% Execute the current command on this directory
- Config3 = execute(Command, AllModules, Config2, File, Env),
-
- %% Execute any after_command plugins on this directory
- execute_post(Command, PluginModules, Config3, File, Env)
- end;
-maybe_execute(_Dir, _Command, Config, _Modules, _PluginModules, _AllModules,
- _File, would_not_cd) ->
- Config.
-
-remember_cwd_predirs(Cwd, Predirs) ->
- Store = fun(Dir, Dict) ->
- case dict:find(Dir, Dict) of
- error ->
- ?DEBUG("Associate sub_dir ~s with ~s~n",
- [Dir, Cwd]),
- dict:store(Dir, Cwd, Dict);
- {ok, Existing} ->
- ?ABORT("Internal consistency assertion failed.~n"
- "sub_dir ~s already associated with ~s.~n"
- "Duplicate sub_dirs or deps entries?",
- [Dir, Existing])
- end
- end,
- lists:foldl(Store, dict:new(), Predirs).
-
-maybe_load_local_config(Dir, ParentConfig) ->
- %% We need to ensure we don't overwrite custom
- %% config when we are dealing with base_dir.
- case rebar_utils:processing_base_dir(ParentConfig, Dir) of
- true ->
- ParentConfig;
- false ->
- rebar_config:new(ParentConfig)
- end.
-
-%%
-%% Given a list of directories and a set of previously processed directories,
-%% process each one we haven't seen yet
-%%
-process_each([], _Command, Config, DirSet, _File) ->
- %% reset cached (setup_env) envs
- Config1 = rebar_config:reset_envs(Config),
- {Config1, DirSet};
-process_each([Dir | Rest], Command, Config, DirSet, File) ->
- case sets:is_element(Dir, DirSet) of
- true ->
- ?DEBUG("Skipping ~s; already processed!\n", [Dir]),
- process_each(Rest, Command, Config, DirSet, File);
- false ->
- {Config1, DirSet2} = process_dir(Dir, Command, Config, DirSet),
- Config2 = rebar_config:clean_config(Config, Config1),
- %% reset cached (setup_env) envs
- Config3 = rebar_config:reset_envs(Config2),
- process_each(Rest, Command, Config3, DirSet2, File)
- end.
-
-%%
-%% Given a list of module sets from rebar.app and a directory, find
-%% the appropriate subset of modules for this directory
-%%
-choose_module_set([], _Dir) ->
- {[], undefined};
-choose_module_set([{Type, Modules} | Rest], Dir) ->
- case is_dir_type(Type, Dir) of
- {true, File} ->
- {Modules, File};
- false ->
- choose_module_set(Rest, Dir)
- end.
-
-is_dir_type(app_dir, Dir) ->
- rebar_app_utils:is_app_dir(Dir);
-is_dir_type(rel_dir, Dir) ->
- rebar_rel_utils:is_rel_dir(Dir);
-is_dir_type(_, _) ->
- false.
-
-execute_pre(Command, Modules, Config, ModuleFile, Env) ->
- execute_plugin_hook("pre_", Command, Modules,
- Config, ModuleFile, Env).
-
-execute_post(Command, Modules, Config, ModuleFile, Env) ->
- execute_plugin_hook("post_", Command, Modules,
- Config, ModuleFile, Env).
-
-execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) ->
- HookFunction = list_to_atom(Hook ++ atom_to_list(Command)),
- execute(HookFunction, hook, Modules, Config, ModuleFile, Env).
-
-%%
-%% Execute a command across all applicable modules
-%%
-execute(Command, Modules, Config, ModuleFile, Env) ->
- execute(Command, not_a_hook, Modules, Config, ModuleFile, Env).
-
-execute(Command, Type, Modules, Config, ModuleFile, Env) ->
- case select_modules(Modules, Command, []) of
- [] ->
- case Type of
- hook ->
- ok;
- not_a_hook ->
- ?WARN("'~p' command does not apply to directory ~s\n",
- [Command, rebar_utils:get_cwd()])
- end,
- Config;
-
- TargetModules ->
- %% Provide some info on where we are
- Dir = rebar_utils:get_cwd(),
- ?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]),
-
- Config1 = increment_operations(Config),
-
- %% Run the available modules
- apply_hooks(pre_hooks, Config1, Command, Env),
- case catch(run_modules(TargetModules, Command,
- Config1, ModuleFile)) of
- {ok, NewConfig} ->
- apply_hooks(post_hooks, NewConfig, Command, Env),
- NewConfig;
- {error, failed} ->
- ?FAIL;
- {Module, {error, _} = Other} ->
- ?ABORT("~p failed while processing ~s in module ~s: ~s\n",
- [Command, Dir, Module,
- io_lib:print(Other, 1, 80, -1)]);
- Other ->
- ?ABORT("~p failed while processing ~s: ~s\n",
- [Command, Dir, io_lib:print(Other, 1, 80, -1)])
- end
- end.
-
-%% Increment the count of operations, since some module
-%% responds to this command
-increment_operations(Config) ->
- Operations = get_operations(Config),
- rebar_config:set_xconf(Config, operations, Operations + 1).
-
-get_operations(Config) ->
- rebar_config:get_xconf(Config, operations).
-
-update_code_path(Config) ->
- case rebar_config:get_local(Config, lib_dirs, []) of
- [] ->
- no_change;
- Paths ->
- LibPaths = expand_lib_dirs(Paths, rebar_utils:get_cwd(), []),
- ok = code:add_pathsa(LibPaths),
- %% track just the paths we added, so we can remove them without
- %% removing other paths added by this dep
- {added, LibPaths}
- end.
-
-restore_code_path(no_change) ->
- ok;
-restore_code_path({added, Paths}) ->
- %% Verify that all of the paths still exist -- some dynamically
- %% added paths can get blown away during clean.
- _ = [code:del_path(F) || F <- Paths, erl_prim_loader_is_file(F)],
- ok.
-
-erl_prim_loader_is_file(File) ->
- erl_prim_loader:read_file_info(File) =/= error.
+process_commands(Commands, ParentConfig) ->
+ true = rebar_utils:expand_code_path(),
+ LibDirs = rebar_config:get_local(ParentConfig, lib_dirs, ["apps", "libs", "."]),
+ DepsDir = rebar_deps:get_deps_dir(ParentConfig),
+ _UpdatedCodePaths = update_code_path([DepsDir | LibDirs]),
+
+ ParentConfig2 = rebar_app_discover:do(ParentConfig, LibDirs),
+ TargetProviders = rebar_provider:get_target_providers(Commands, ParentConfig2),
+ ParentConfig3 =
+ lists:foldl(fun(TargetProvider, Conf) ->
+ Provider = rebar_provider:get_provider(TargetProvider, rebar_config:providers(Conf)),
+ {ok, Conf1} = rebar_provider:do(Provider, Conf),
+ Conf1
+ end, ParentConfig2, TargetProviders).
+
+update_code_path([]) ->
+ no_change;
+update_code_path(Paths) ->
+ LibPaths = expand_lib_dirs(Paths, rebar_utils:get_cwd(), []),
+ ok = code:add_pathsa(LibPaths),
+ %% track just the paths we added, so we can remove them without
+ %% removing other paths added by this dep
+ {added, LibPaths}.
expand_lib_dirs([], _Root, Acc) ->
Acc;
@@ -471,187 +87,12 @@ expand_lib_dirs([Dir | Rest], Root, Acc) ->
end,
expand_lib_dirs(Rest, Root, Acc ++ FqApps).
-
-
-select_modules([], _Command, Acc) ->
- lists:reverse(Acc);
-select_modules([Module | Rest], Command, Acc) ->
- {module, Module} = code:ensure_loaded(Module),
- case erlang:function_exported(Module, Command, 2) of
- true ->
- select_modules(Rest, Command, [Module | Acc]);
- false ->
- select_modules(Rest, Command, Acc)
- end.
-
-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.
-
-apply_hooks(Mode, Config, Command, Env0) ->
- Hooks = rebar_config:get_local(Config, Mode, []),
- Env = rebar_utils:patch_env(Config, Env0),
- lists:foreach(fun apply_hook/1,
- [{Env, Hook} || Hook <- Hooks,
- element(1, Hook) =:= Command orelse
- element(2, Hook) =:= Command]).
-
-apply_hook({Env, {Arch, Command, Hook}}) ->
- case rebar_utils:is_arch(Arch) of
- true ->
- apply_hook({Env, {Command, Hook}});
- false ->
- ok
- end;
-apply_hook({Env, {Command, Hook}}) ->
- Msg = lists:flatten(io_lib:format("Command [~p] failed!~n", [Command])),
- rebar_utils:sh(Hook, [{env, Env}, {abort_on_error, Msg}]).
-
-setup_envs(Config, Modules) ->
- lists:foldl(fun(M, {C,E}=T) ->
- case erlang:function_exported(M, setup_env, 1) of
- true ->
- Env = M:setup_env(C),
- C1 = rebar_config:save_env(C, M, Env),
- {C1, E++Env};
- false ->
- T
- end
- end, {Config, []}, Modules).
-
-acc_modules(Modules, Command, Config, File) ->
- acc_modules(select_modules(Modules, Command, []),
- Command, Config, File, []).
-
-acc_modules([], _Command, Config, _File, Acc) ->
- {Config, Acc};
-acc_modules([Module | Rest], Command, Config, File, Acc) ->
- {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.
-%%
-plugin_modules(Config, PredirsAssoc) ->
- Modules = lists:flatten(rebar_config:get_all(Config, plugins)),
- ?DEBUG("Plugins requested while processing ~s: ~p~n",
- [rebar_utils:get_cwd(), Modules]),
- plugin_modules(Config, PredirsAssoc, ulist(Modules)).
-
-ulist(L) ->
- ulist(L, []).
-
-ulist([], Acc) ->
- lists:reverse(Acc);
-ulist([H | T], Acc) ->
- case lists:member(H, Acc) of
- true ->
- ulist(T, Acc);
- false ->
- ulist(T, [H | Acc])
- end.
-
-plugin_modules(_Config, _PredirsAssoc, []) ->
- {ok, []};
-plugin_modules(Config, PredirsAssoc, Modules) ->
- FoundModules = [M || M <- Modules, code:which(M) =/= non_existing],
- plugin_modules(Config, PredirsAssoc, FoundModules, Modules -- FoundModules).
-
-plugin_modules(_Config, _PredirsAssoc, FoundModules, []) ->
- {ok, FoundModules};
-plugin_modules(Config, PredirsAssoc, FoundModules, MissingModules) ->
- {Loaded, NotLoaded} = load_plugin_modules(Config, PredirsAssoc,
- MissingModules),
- AllViablePlugins = FoundModules ++ Loaded,
- case NotLoaded =/= [] of
+maybe_load_local_config(Dir, ParentConfig) ->
+ %% We need to ensure we don't overwrite custom
+ %% config when we are dealing with base_dir.
+ case rebar_utils:processing_base_dir(ParentConfig, Dir) of
true ->
- %% NB: we continue to ignore this situation, as did the
- %% original code
- ?WARN("Missing plugins: ~p\n", [NotLoaded]);
- false ->
- ?DEBUG("Loaded plugins: ~p~n", [AllViablePlugins]),
- ok
- end,
- {ok, AllViablePlugins}.
-
-load_plugin_modules(Config, PredirsAssoc, Modules) ->
- Cwd = rebar_utils:get_cwd(),
- PluginDirs = get_all_plugin_dirs(Config, Cwd, PredirsAssoc),
- ?DEBUG("Plugin dirs for ~s:~n~p~n", [Cwd, PluginDirs]),
-
- %% Find relevant sources in base_dir and plugin_dir
- RE = string:join([atom_to_list(M)++"\\.erl" || M <- Modules], "|"),
- %% If a plugin is found both in base_dir and plugin_dir, the clash
- %% will provoke an error and we'll abort.
- Sources = [rebar_utils:find_files(PD, RE, false) || PD <- PluginDirs],
-
- %% Compile and load plugins
- Loaded = [load_plugin(Src) || Src <- lists:append(Sources)],
- FilterMissing = is_missing_plugin(Loaded),
- NotLoaded = [V || V <- Modules, FilterMissing(V)],
- {Loaded, NotLoaded}.
-
-get_all_plugin_dirs(Config, Cwd, PredirsAssoc) ->
- [rebar_utils:get_cwd()]
- ++ get_plugin_dir(Config, Cwd)
- ++ get_base_plugin_dirs(Cwd, PredirsAssoc).
-
-get_plugin_dir(Config, Cwd) ->
- case rebar_config:get_local(Config, plugin_dir, undefined) of
- undefined ->
- %% Plugin can be in the project's "plugins" folder
- [filename:join(Cwd, "plugins")];
- Dir ->
- [Dir]
- end.
-
-%% We also want to include this case:
-%% Plugin can be in "plugins" directory of the plugin base directory.
-%% For example, Cwd depends on Plugin, and deps/Plugin/plugins/Plugin.erl
-%% is the plugin.
-get_base_plugin_dirs(Cwd, PredirsAssoc) ->
- [filename:join(Dir, "plugins") ||
- Dir <- get_plugin_base_dirs(Cwd, PredirsAssoc)].
-
-%% @doc PredirsAssoc is a dictionary of plugindir -> 'parent' pairs.
-%% 'parent' in this case depends on plugin; therefore we have to give
-%% all plugins that Cwd ('parent' in this case) depends on.
-get_plugin_base_dirs(Cwd, PredirsAssoc) ->
- [PluginDir || {PluginDir, Master} <- dict:to_list(PredirsAssoc),
- Master =:= Cwd].
-
-is_missing_plugin(Loaded) ->
- fun(Mod) -> not lists:member(Mod, Loaded) end.
-
-load_plugin(Src) ->
- case compile:file(Src, [binary, return_errors]) of
- {ok, Mod, Bin} ->
- load_plugin_module(Mod, Bin, Src);
- {error, Errors, _Warnings} ->
- ?ABORT("Plugin ~s contains compilation errors: ~p~n",
- [Src, Errors])
- end.
-
-load_plugin_module(Mod, Bin, Src) ->
- case code:is_loaded(Mod) of
- {file, Loaded} ->
- ?ABORT("Plugin ~p clashes with previously loaded module ~p~n",
- [Mod, Loaded]);
+ ParentConfig;
false ->
- ?INFO("Loading plugin ~p from ~s~n", [Mod, Src]),
- {module, Mod} = code:load_binary(Mod, Src, Bin),
- Mod
+ rebar_config:new(ParentConfig)
end.
diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl
index bd94921..d9d0399 100644
--- a/src/rebar_deps.erl
+++ b/src/rebar_deps.erl
@@ -26,113 +26,109 @@
%% -------------------------------------------------------------------
-module(rebar_deps).
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
-include("rebar.hrl").
--export([preprocess/2,
- postprocess/2,
- compile/2,
- setup_env/1,
- 'check-deps'/2,
- 'get-deps'/2,
- 'update-deps'/2,
- 'delete-deps'/2,
- 'list-deps'/2]).
+-export([setup_env/1]).
%% for internal use only
-export([info/2]).
-export([get_deps_dir/1]).
--record(dep, { dir,
- app,
- vsn_regex,
- source,
- is_raw }). %% is_raw = true means non-Erlang/OTP dependency
+-define(PROVIDER, deps).
+-define(DEPS, []).
%% ===================================================================
%% Public API
%% ===================================================================
-preprocess(Config, _) ->
- %% Side effect to set deps_dir globally for all dependencies from
- %% top level down. Means the root deps_dir is honoured or the default
- %% used globally since it will be set on the first time through here
- Config1 = set_shared_deps_dir(Config, get_shared_deps_dir(Config, [])),
-
- %% Get the list of deps for the current working directory and identify those
- %% deps that are available/present.
- Deps = rebar_config:get_local(Config1, deps, []),
- {Config2, {AvailableDeps, MissingDeps}} = find_deps(Config1, find, Deps),
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = deps,
+ bare = false,
+ deps = ?DEPS,
+ example = "rebar deps",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ Deps = rebar_config:get_local(Config, deps, []),
+ Goals = lists:map(fun({Name, "", _}) ->
+ Name;
+ ({Name, ".*", _}) ->
+ Name;
+ ({Name, Vsn, _}) ->
+ {Name, Vsn}
+ end, Deps),
- ?DEBUG("Available deps: ~p\n", [AvailableDeps]),
- ?DEBUG("Missing deps : ~p\n", [MissingDeps]),
+ {Config1, Deps1} = update_deps(Config, Deps),
+ Config2 = rebar_config:deps(Config1, Deps1),
- %% Add available deps to code path
- Config3 = update_deps_code_path(Config2, AvailableDeps),
+ {ok, rebar_config:goals(Config2, Goals)}.
- %% Filtering out 'raw' dependencies so that no commands other than
- %% deps-related can be executed on their directories.
- NonRawAvailableDeps = [D || D <- AvailableDeps, not D#dep.is_raw],
+update_deps(Config, Deps) ->
+ DepsDir = get_deps_dir(Config),
- case rebar_config:get_xconf(Config, current_command, undefined) of
- 'update-deps' ->
- %% Skip ALL of the dep folders, we do this because we don't want
- %% any other calls to preprocess() for update-deps beyond the
- %% toplevel directory. They aren't actually harmful, but they slow
- %% things down unnecessarily.
- NewConfig = lists:foldl(
- fun(D, Acc) ->
- rebar_config:set_skip_dir(Acc, D#dep.dir)
- end,
- Config3,
- collect_deps(rebar_utils:get_cwd(), Config3)),
- %% Return the empty list, as we don't want anything processed before
- %% us.
- {ok, NewConfig, []};
- _ ->
- %% 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.
- %%
- %% Also, if skip_deps=comma,separated,app,list, then only the given
- %% dependencies are skipped.
- NewConfig =
- case rebar_config:get_global(Config3, skip_deps, false) of
- "true" ->
- lists:foldl(
- fun(#dep{dir = Dir}, C) ->
- rebar_config:set_skip_dir(C, Dir)
- end, Config3, AvailableDeps);
- Apps when is_list(Apps) ->
- SkipApps = [list_to_atom(App) ||
- App <- string:tokens(Apps, ",")],
- lists:foldl(
- fun(#dep{dir = Dir, app = App}, C) ->
- case lists:member(App, SkipApps) of
- true -> rebar_config:set_skip_dir(C, Dir);
- false -> C
- end
- end, Config3, AvailableDeps);
- _ ->
- Config3
- end,
+ %% Find available apps to fulfill dependencies
+ FoundApps = rebar_app_discover:find_apps([DepsDir]),
- %% Return all the available dep directories for process
- {ok, NewConfig, dep_dirs(NonRawAvailableDeps)}
- end.
+ %% Resolve deps and their dependencies
+ Deps1 = handle_deps(Deps, FoundApps),
-postprocess(Config, _) ->
- case rebar_config:get_xconf(Config, ?MODULE, undefined) of
- undefined ->
- {ok, []};
- Dirs ->
- NewConfig = rebar_config:erase_xconf(Config, ?MODULE),
- {ok, NewConfig, Dirs}
+ case download_missing_deps(Config, DepsDir, FoundApps, Deps1) of
+ {Config1, []} ->
+ {Config1, Deps1};
+ {Config1, _} ->
+ update_deps(Config1, Deps1)
end.
-compile(Config, _) ->
- {Config1, _AvailDeps} = do_check_deps(Config),
- {ok, Config1}.
+handle_deps(Deps, Found) ->
+ NewDeps =
+ lists:foldl(fun(X, DepsAcc) ->
+ C = rebar_config:new2(rebar_config:new(), rebar_app_info:dir(X)),
+ LocalDeps = rebar_config:get_local(C, deps, []),
+ [LocalDeps | DepsAcc]
+ end, [], Found),
+ NewDeps1 = lists:flatten(NewDeps),
+
+ %% Weed out duplicates
+ lists:umerge(fun(A, B) ->
+ element(1, A) =:= element(1, B)
+ end, lists:usort(Deps), lists:usort(NewDeps1)).
+
+download_missing_deps(Config, DepsDir, Found, Deps) ->
+ Apps = rebar_config:apps_to_build(Config),
+ Missing = lists:filter(fun(X) ->
+ not lists:any(fun(F) ->
+ element(1, X) =:= element(2, F)
+ end, Found++Apps)
+ end, Deps),
+ ec_plists:foreach(fun(X) ->
+ TargetDir = get_deps_dir(DepsDir, element(1, X)),
+ case filelib:is_dir(TargetDir) of
+ true ->
+ ok;
+ false ->
+ rebar_fetch:download_source(TargetDir, element(3, X))
+ end
+ end, Missing),
+
+ Config1 = lists:foldl(fun(X, ConfigAcc) ->
+ TargetDir = get_deps_dir(DepsDir, element(1, X)),
+ [AppSrc] = rebar_app_discover:find_apps([TargetDir]),
+ rebar_config:add_dep(ConfigAcc, AppSrc)
+ end, Config, Missing),
+
+ {Config1, Missing}.
%% set REBAR_DEPS_DIR and ERL_LIBS environment variables
setup_env(Config) ->
@@ -152,99 +148,77 @@ setup_env(Config) ->
end,
[{"REBAR_DEPS_DIR", DepsDir}, ERL_LIBS].
-%% common function used by 'check-deps' and 'compile'
-do_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(Config, find, Deps) of
- {Config1, {AvailDeps, []}} ->
- %% No missing deps
- {Config1, AvailDeps};
- {_Config1, {_, MissingDeps}} ->
- lists:foreach(fun (#dep{app=App, vsn_regex=Vsn, source=Src}) ->
- ?CONSOLE("Dependency not available: "
- "~p-~s (~p)\n", [App, Vsn, Src])
- end, MissingDeps),
- ?FAIL
- end.
-
-'check-deps'(Config, _) ->
- {Config1, AvailDeps} = do_check_deps(Config),
- {ok, save_dep_dirs(Config1, AvailDeps)}.
-
-'get-deps'(Config, _) ->
- %% Determine what deps are available and missing
- Deps = rebar_config:get_local(Config, 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.
- {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
- {ok, save_dep_dirs(Config2, lists:reverse(PulledDeps))}.
-
-'update-deps'(Config, _) ->
- Config1 = rebar_config:set_xconf(Config, depowner, dict:new()),
- {Config2, UpdatedDeps} = update_deps_int(Config1, []),
- DepOwners = rebar_config:get_xconf(Config2, depowner, dict:new()),
+get_deps_dir(Config) ->
+ BaseDir = rebar_utils:base_dir(Config),
+ get_deps_dir(BaseDir, "deps").
- %% check for conflicting deps
- _ = [?ERROR("Conflicting dependencies for ~p: ~p~n",
- [K, [{"From: " ++ string:join(dict:fetch(D, DepOwners), ", "),
- {D#dep.vsn_regex, D#dep.source}} || D <- V]])
- || {K, V} <- dict:to_list(
- lists:foldl(
- fun(Dep, Acc) ->
- dict:append(Dep#dep.app, Dep, Acc)
- end, dict:new(), UpdatedDeps)),
- length(V) > 1],
+%% ===================================================================
+%% Internal functions
+%% ===================================================================
- %% Add each updated dep to our list of dirs for post-processing. This yields
- %% the necessary transitivity of the deps
- {ok, save_dep_dirs(Config, UpdatedDeps)}.
+get_deps_dir(DepsDir, App) ->
+ filename:join(DepsDir, App).
+
+-spec gather_application_info(file:name(), file:filename()) ->
+ {ok, rebar_app_info:t()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()}.
+gather_application_info(EbinDir, File) ->
+ AppDir = filename:dirname(EbinDir),
+ case file:consult(File) of
+ {ok, [{application, AppName, AppDetail}]} ->
+ validate_application_info(EbinDir, File, AppName, AppDetail);
+ {error, Reason} ->
+ {warning, {unable_to_load_app, AppDir, Reason}};
+ _ ->
+ {warning, {invalid_app_file, File}}
+ end.
-'delete-deps'(Config, _) ->
- %% Delete all the available deps in our deps/ directory, if any
- {true, DepsDir} = get_deps_dir(Config),
- Deps = rebar_config:get_local(Config, deps, []),
- {Config1, {AvailableDeps, _}} = find_deps(Config, find, Deps),
- _ = [delete_dep(D)
- || D <- AvailableDeps,
- lists:prefix(DepsDir, D#dep.dir)],
- {ok, Config1}.
+-spec validate_application_info(file:name(),
+ file:name(),
+ atom(),
+ proplists:proplist()) ->
+ {ok, list()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()}.
+validate_application_info(EbinDir, AppFile, AppName, AppDetail) ->
+ AppDir = filename:dirname(EbinDir),
+ case get_modules_list(AppFile, AppDetail) of
+ {ok, List} ->
+ has_all_beams(EbinDir, List);
+ Error ->
+ Error
+ end.
-'list-deps'(Config, _) ->
- Deps = rebar_config:get_local(Config, deps, []),
- case find_deps(Config, find, Deps) of
- {Config1, {AvailDeps, []}} ->
- lists:foreach(fun(Dep) -> print_source(Dep) end, AvailDeps),
- {ok, save_dep_dirs(Config1, AvailDeps)};
- {_, MissingDeps} ->
- ?ABORT("Missing dependencies: ~p\n", [MissingDeps])
+-spec get_modules_list(file:name(), proplists:proplist()) ->
+ {ok, list()} |
+ {warning, Reason::term()} |
+ {error, Reason::term()}.
+get_modules_list(AppFile, AppDetail) ->
+ case proplists:get_value(modules, AppDetail) of
+ undefined ->
+ {warning, {invalid_app_file, AppFile}};
+ ModulesList ->
+ {ok, ModulesList}
end.
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
+-spec has_all_beams(file:name(), list()) ->
+ ok | {error, Reason::term()}.
+has_all_beams(EbinDir, [Module | ModuleList]) ->
+ BeamFile = filename:join([EbinDir,
+ list_to_binary(atom_to_list(Module) ++ ".beam")]),
+ case filelib:is_file(BeamFile) of
+ true ->
+ has_all_beams(EbinDir, ModuleList);
+ false ->
+ {warning, {missing_beam_file, Module, BeamFile}}
+ end;
+has_all_beams(_, []) ->
+ ok.
-info(help, compile) ->
- info_help("Display to be fetched dependencies");
-info(help, 'check-deps') ->
- info_help("Display to be fetched dependencies");
-info(help, 'get-deps') ->
- info_help("Fetch dependencies");
-info(help, 'update-deps') ->
- info_help("Update fetched dependencies");
-info(help, 'delete-deps') ->
- info_help("Delete fetched dependencies");
-info(help, 'list-deps') ->
- info_help("List dependencies").
+info(help, 'deps') ->
+ info_help("Display dependencies").
info_help(Description) ->
?CONSOLE(
@@ -281,540 +255,3 @@ info_help(Description) ->
{app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}},
{app_name, ".*", {p4, "//depot/subdir/app_dir"}}]}
]).
-
-%% Added because of trans deps,
-%% need all deps in same dir and should be the one set by the root rebar.config
-%% In case one is given globally, it has higher priority
-%% Sets a default if root config has no deps_dir set
-set_shared_deps_dir(Config, []) ->
- LocalDepsDir = rebar_config:get_local(Config, deps_dir, "deps"),
- GlobalDepsDir = rebar_config:get_global(Config, deps_dir, LocalDepsDir),
- DepsDir = case os:getenv("REBAR_DEPS_DIR") of
- false ->
- GlobalDepsDir;
- Dir ->
- Dir
- end,
- rebar_config:set_xconf(Config, deps_dir, DepsDir);
-set_shared_deps_dir(Config, _DepsDir) ->
- Config.
-
-get_shared_deps_dir(Config, Default) ->
- rebar_config:get_xconf(Config, deps_dir, Default).
-
-get_deps_dir(Config) ->
- get_deps_dir(Config, "").
-
-get_deps_dir(Config, App) ->
- BaseDir = rebar_utils:base_dir(Config),
- DepsDir = get_shared_deps_dir(Config, "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
- %% to match get_deps_dir's return type
- case code:lib_dir(App) of
- {error, bad_name} -> {false, bad_name};
- Path -> {true, Path}
- end.
-
-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, Dep#dep.is_raw) 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(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) ->
- find_deps(Config, Mode, [{App, VsnRegex, Source, []} | Rest], Acc);
-find_deps(Config, Mode, [{App, VsnRegex, Source, Opts} | Rest], Acc)
- when is_list(Opts) ->
- Dep = #dep { app = App,
- vsn_regex = VsnRegex,
- source = Source,
- %% dependency is considered raw (i.e. non-Erlang/OTP) when
- %% 'raw' option is present
- is_raw = proplists:get_value(raw, Opts, false) },
- {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(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(Config, Dep, Dep#dep.source).
-
-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(Config, Dep, get_deps_dir(Config, 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(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(Config, Dep, get_deps_dir(Config, Dep#dep.app)).
-
-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,
- IsRaw = Dep#dep.is_raw,
- case is_app_available(Config, App, VsnRegex, Dir, IsRaw) of
- {Config1, {true, _AppFile}} -> {Config1, {avail, Dir}};
- {Config1, {false, _}} -> {Config1, {missing, Dir}}
- end.
-
-acc_deps(find, avail, Dep, AppDir, {Avail, Missing}) ->
- {[Dep#dep { dir = AppDir } | Avail], Missing};
-acc_deps(find, missing, Dep, AppDir, {Avail, Missing}) ->
- {Avail, [Dep#dep { dir = AppDir } | Missing]};
-acc_deps(read, _, Dep, AppDir, Acc) ->
- [Dep#dep { dir = AppDir } | Acc].
-
-delete_dep(D) ->
- case filelib:is_dir(D#dep.dir) of
- true ->
- ?INFO("Deleting dependency: ~s\n", [D#dep.dir]),
- rebar_file_utils:rm_rf(D#dep.dir);
- false ->
- ok
- end.
-
-require_source_engine(Source) ->
- true = source_engine_avail(Source),
- ok.
-
-%% IsRaw = false means regular Erlang/OTP dependency
-%%
-%% IsRaw = true means non-Erlang/OTP dependency, e.g. the one that does not
-%% have a proper .app file
-is_app_available(Config, App, VsnRegex, Path, _IsRaw = false) ->
- ?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(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 ->
- {Config2, {true, Path}};
- nomatch ->
- ?WARN("~s has version ~p; requested regex was ~s\n",
- [AppFile, Vsn, VsnRegex]),
- {Config2,
- {false, {version_mismatch,
- {AppFile,
- {expected, VsnRegex}, {has, Vsn}}}}}
- end;
- {Config1, OtherApp} ->
- ?WARN("~s has application id ~p; expected ~p\n",
- [AppFile, OtherApp, App]),
- {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]),
- {Config, {false, {missing_app_file, Path}}}
- end;
-is_app_available(Config, App, _VsnRegex, Path, _IsRaw = true) ->
- ?DEBUG("is_app_available, looking for Raw Depencency ~p with Path ~p~n",
- [App, Path]),
- case filelib:is_dir(Path) of
- true ->
- %% TODO: look for version string in <Path>/VERSION file? Not clear
- %% how to detect git/svn/hg/{cmd, ...} settings that can be passed
- %% to rebar_utils:vcs_vsn/2 to obtain version dynamically
- {Config, {true, Path}};
- false ->
- ?WARN("Expected ~s to be a raw dependency directory, "
- "but no directory found.\n", [Path]),
- {Config, {false, {missing_raw_dependency_directory, Path}}}
- end.
-
-use_source(Config, Dep) ->
- use_source(Config, Dep, 3).
-
-use_source(_Config, Dep, 0) ->
- ?ABORT("Failed to acquire source from ~p after 3 tries.\n",
- [Dep#dep.source]);
-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(Config, Dep#dep.app, Dep#dep.vsn_regex,
- Dep#dep.dir, Dep#dep.is_raw) of
- {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),
- {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 "
- "with reason:~n~p.\n", [Dep#dep.dir, Reason])
- end;
- false ->
- ?CONSOLE("Pulling ~p from ~p\n", [Dep#dep.app, Dep#dep.source]),
- require_source_engine(Dep#dep.source),
- {true, TargetDir} = get_deps_dir(Config, Dep#dep.app),
- download_source(TargetDir, Dep#dep.source),
- use_source(Config, Dep#dep { dir = TargetDir }, Count-1)
- end.
-
--record(p4_settings, {
- client=undefined,
- transport="tcp4:perforce:1666",
- username,
- password
- }).
-init_p4_settings(Basename) ->
- #p4_settings{client =
- case inet:gethostname() of
- {ok,HostName} ->
- HostName ++ "-"
- ++ os:getenv("USER") ++ "-"
- ++ Basename
- ++ "-Rebar-automated-download"
- end}.
-
-download_source(AppDir, {p4, Url}) ->
- download_source(AppDir, {p4, Url, "#head"});
-download_source(AppDir, {p4, Url, Rev}) ->
- download_source(AppDir, {p4, Url, Rev, init_p4_settings(filename:basename(AppDir))});
-download_source(AppDir, {p4, Url, _Rev, Settings}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh_send("p4 client -i",
- ?FMT("Client: ~s~n"
- ++"Description: generated by Rebar~n"
- ++"Root: ~s~n"
- ++"View:~n"
- ++" ~s/... //~s/...~n",
- [Settings#p4_settings.client,
- AppDir,
- Url,
- Settings#p4_settings.client]),
- []),
- rebar_utils:sh(?FMT("p4 -c ~s sync -f", [Settings#p4_settings.client]), []);
-download_source(AppDir, {hg, Url, Rev}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]),
- [{cd, filename:dirname(AppDir)}]),
- rebar_utils:sh(?FMT("hg update ~s", [Rev]), [{cd, AppDir}]);
-download_source(AppDir, {git, Url}) ->
- download_source(AppDir, {git, Url, {branch, "HEAD"}});
-download_source(AppDir, {git, Url, ""}) ->
- download_source(AppDir, {git, Url, {branch, "HEAD"}});
-download_source(AppDir, {git, Url, {branch, Branch}}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
- [{cd, filename:dirname(AppDir)}]),
- rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [{cd, AppDir}]);
-download_source(AppDir, {git, Url, {tag, Tag}}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
- [{cd, filename:dirname(AppDir)}]),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [{cd, AppDir}]);
-download_source(AppDir, {git, Url, Rev}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
- [{cd, filename:dirname(AppDir)}]),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, AppDir}]);
-download_source(AppDir, {bzr, Url, Rev}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("bzr branch -r ~s ~s ~s",
- [Rev, Url, filename:basename(AppDir)]),
- [{cd, filename:dirname(AppDir)}]);
-download_source(AppDir, {svn, Url, Rev}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("svn checkout -r ~s ~s ~s",
- [Rev, Url, filename:basename(AppDir)]),
- [{cd, filename:dirname(AppDir)}]);
-download_source(AppDir, {rsync, Url}) ->
- ok = filelib:ensure_dir(AppDir),
- rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s", [Url, AppDir]), []);
-download_source(AppDir, {fossil, Url}) ->
- download_source(AppDir, {fossil, Url, ""});
-download_source(AppDir, {fossil, Url, Version}) ->
- Repository = filename:join(AppDir, filename:basename(AppDir) ++ ".fossil"),
- ok = filelib:ensure_dir(Repository),
- ok = file:set_cwd(AppDir),
- rebar_utils:sh(?FMT("fossil clone ~s ~s", [Url, Repository]),
- [{cd, AppDir}]),
- rebar_utils:sh(?FMT("fossil open ~s ~s --nested", [Repository, Version]),
- []).
-
-update_source(Config, Dep) ->
- %% It's possible when updating a source, that a given dep does not have a
- %% VCS directory, such as when a source archive is built of a project, with
- %% all deps already downloaded/included. So, verify that the necessary VCS
- %% directory exists before attempting to do the update.
- {true, AppDir} = get_deps_dir(Config, Dep#dep.app),
- case has_vcs_dir(element(1, Dep#dep.source), AppDir) of
- true ->
- ?CONSOLE("Updating ~p from ~p\n", [Dep#dep.app, Dep#dep.source]),
- require_source_engine(Dep#dep.source),
- update_source1(AppDir, Dep#dep.source),
- Dep;
- false ->
- ?WARN("Skipping update for ~p: "
- "no VCS directory available!\n", [Dep]),
- Dep
- end.
-
-update_source1(AppDir, Args) when element(1, Args) =:= p4 ->
- download_source(AppDir, Args);
-update_source1(AppDir, {git, Url}) ->
- update_source1(AppDir, {git, Url, {branch, "HEAD"}});
-update_source1(AppDir, {git, Url, ""}) ->
- update_source1(AppDir, {git, Url, {branch, "HEAD"}});
-update_source1(AppDir, {git, _Url, {branch, Branch}}) ->
- ShOpts = [{cd, AppDir}],
- rebar_utils:sh("git fetch origin", ShOpts),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Branch]), ShOpts),
- rebar_utils:sh(
- ?FMT("git pull --ff-only --no-rebase -q origin ~s", [Branch]),ShOpts);
-update_source1(AppDir, {git, _Url, {tag, Tag}}) ->
- ShOpts = [{cd, AppDir}],
- rebar_utils:sh("git fetch origin", ShOpts),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), ShOpts);
-update_source1(AppDir, {git, _Url, Refspec}) ->
- ShOpts = [{cd, AppDir}],
- rebar_utils:sh("git fetch origin", ShOpts),
- rebar_utils:sh(?FMT("git checkout -q ~s", [Refspec]), ShOpts);
-update_source1(AppDir, {svn, _Url, Rev}) ->
- rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [{cd, AppDir}]);
-update_source1(AppDir, {hg, _Url, Rev}) ->
- rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [{cd, AppDir}]);
-update_source1(AppDir, {bzr, _Url, Rev}) ->
- rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [{cd, AppDir}]);
-update_source1(AppDir, {rsync, Url}) ->
- rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]);
-update_source1(AppDir, {fossil, Url}) ->
- update_source1(AppDir, {fossil, Url, ""});
-update_source1(AppDir, {fossil, _Url, Version}) ->
- ok = file:set_cwd(AppDir),
- rebar_utils:sh("fossil pull", [{cd, AppDir}]),
- rebar_utils:sh(?FMT("fossil update ~s", [Version]), []).
-
-%% Recursively update deps, this is not done via rebar's usual dep traversal as
-%% that is the wrong order (tips are updated before branches). Instead we do a
-%% traverse the deps at each level completely before traversing *their* deps.
-%% This allows updates to actually propogate down the tree, rather than fail to
-%% flow up the tree, which was the previous behaviour.
-update_deps_int(Config0, UDD) ->
- %% Determine what deps are required
- ConfDir = filename:basename(rebar_utils:get_cwd()),
- RawDeps = rebar_config:get_local(Config0, deps, []),
- {Config1, Deps} = find_deps(Config0, read, RawDeps),
-
- %% Update each dep
- UpdatedDeps = [update_source(Config1, D)
- || D <- Deps, D#dep.source =/= undefined,
- not lists:member(D, UDD),
- not should_skip_update_dep(Config1, D)
- ],
-
- lists:foldl(fun(Dep, {Config, Updated}) ->
- {true, AppDir} = get_deps_dir(Config, Dep#dep.app),
- Config2 = case has_vcs_dir(element(1, Dep#dep.source),
- AppDir) of
- false ->
- %% If the dep did not exist (maybe it
- %% was added), clone it.
- %% We'll traverse ITS deps below and
- %% clone them if needed.
- {C1, _D1} = use_source(Config, Dep),
- C1;
- true ->
- Config
- end,
- ok = file:set_cwd(AppDir),
- Config3 = rebar_config:new(Config2),
- %% track where a dep comes from...
- DepOwner = dict:append(
- Dep, ConfDir,
- rebar_config:get_xconf(Config3, depowner,
- dict:new())),
- Config4 = rebar_config:set_xconf(Config3, depowner,
- DepOwner),
-
- {Config5, Res} = update_deps_int(Config4, Updated),
- {Config5, lists:umerge(lists:sort(Res),
- lists:sort(Updated))}
- end, {Config1, lists:umerge(lists:sort(UpdatedDeps),
- lists:sort(UDD))}, UpdatedDeps).
-
-should_skip_update_dep(Config, Dep) ->
- {true, AppDir} = get_deps_dir(Config, Dep#dep.app),
- case rebar_app_utils:is_app_dir(AppDir) of
- false ->
- false;
- {true, AppFile} ->
- case rebar_app_utils:is_skipped_app(Config, AppFile) of
- {_Config, {true, _SkippedApp}} ->
- true;
- _ ->
- false
- end
- end.
-
-%% Recursively walk the deps and build a list of them.
-collect_deps(Dir, C) ->
- case file:set_cwd(Dir) of
- ok ->
- Config = rebar_config:new(C),
- RawDeps = rebar_config:get_local(Config, deps, []),
- {Config1, Deps} = find_deps(Config, read, RawDeps),
-
- lists:flatten(Deps ++ [begin
- {true, AppDir} = get_deps_dir(
- Config1, Dep#dep.app),
- collect_deps(AppDir, C)
- end || Dep <- Deps]);
- _ ->
- []
- end.
-
-
-%% ===================================================================
-%% Source helper functions
-%% ===================================================================
-
-source_engine_avail(Source) ->
- Name = element(1, Source),
- source_engine_avail(Name, Source).
-
-source_engine_avail(Name, Source)
- when Name == hg; Name == git; Name == svn; Name == bzr; Name == rsync;
- Name == fossil; Name == p4 ->
- case vcs_client_vsn(Name) >= required_vcs_client_vsn(Name) of
- true ->
- true;
- false ->
- ?ABORT("Rebar requires version ~p or higher of ~s to process ~p\n",
- [required_vcs_client_vsn(Name), Name, Source])
- end.
-
-vcs_client_vsn(false, _VsnArg, _VsnRegex) ->
- false;
-vcs_client_vsn(Path, VsnArg, VsnRegex) ->
- {ok, Info} = rebar_utils:sh(Path ++ VsnArg, [{env, [{"LANG", "C"}]},
- {use_stdout, false}]),
- case re:run(Info, VsnRegex, [{capture, all_but_first, list}]) of
- {match, Match} ->
- list_to_tuple([list_to_integer(S) || S <- Match]);
- _ ->
- false
- end.
-
-required_vcs_client_vsn(p4) -> {2013, 1};
-required_vcs_client_vsn(hg) -> {1, 1};
-required_vcs_client_vsn(git) -> {1, 5};
-required_vcs_client_vsn(bzr) -> {2, 0};
-required_vcs_client_vsn(svn) -> {1, 6};
-required_vcs_client_vsn(rsync) -> {2, 0};
-required_vcs_client_vsn(fossil) -> {1, 0}.
-
-vcs_client_vsn(p4) ->
- vcs_client_vsn(rebar_utils:find_executable("p4"), " -V",
- "Rev\\. .*/(\\d+)\\.(\\d)/");
-vcs_client_vsn(hg) ->
- vcs_client_vsn(rebar_utils:find_executable("hg"), " --version",
- "version (\\d+).(\\d+)");
-vcs_client_vsn(git) ->
- vcs_client_vsn(rebar_utils:find_executable("git"), " --version",
- "git version (\\d+).(\\d+)");
-vcs_client_vsn(bzr) ->
- vcs_client_vsn(rebar_utils:find_executable("bzr"), " --version",
- "Bazaar \\(bzr\\) (\\d+).(\\d+)");
-vcs_client_vsn(svn) ->
- vcs_client_vsn(rebar_utils:find_executable("svn"), " --version",
- "svn, version (\\d+).(\\d+)");
-vcs_client_vsn(rsync) ->
- vcs_client_vsn(rebar_utils:find_executable("rsync"), " --version",
- "rsync version (\\d+).(\\d+)");
-vcs_client_vsn(fossil) ->
- vcs_client_vsn(rebar_utils:find_executable("fossil"), " version",
- "version (\\d+).(\\d+)").
-
-has_vcs_dir(p4, _) ->
- true;
-has_vcs_dir(git, Dir) ->
- filelib:is_dir(filename:join(Dir, ".git"));
-has_vcs_dir(hg, Dir) ->
- filelib:is_dir(filename:join(Dir, ".hg"));
-has_vcs_dir(bzr, Dir) ->
- filelib:is_dir(filename:join(Dir, ".bzr"));
-has_vcs_dir(svn, Dir) ->
- filelib:is_dir(filename:join(Dir, ".svn"))
- orelse filelib:is_dir(filename:join(Dir, "_svn"));
-has_vcs_dir(rsync, _) ->
- true;
-has_vcs_dir(_, _) ->
- true.
-
-print_source(#dep{app=App, source=Source}) ->
- ?CONSOLE("~s~n", [format_source(App, Source)]).
-
-format_source(App, {p4, Url}) ->
- format_source(App, {p4, Url, "#head"});
-format_source(App, {git, Url}) ->
- ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
-format_source(App, {git, Url, ""}) ->
- ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
-format_source(App, {git, Url, {branch, Branch}}) ->
- ?FMT("~p BRANCH ~s ~s", [App, Branch, Url]);
-format_source(App, {git, Url, {tag, Tag}}) ->
- ?FMT("~p TAG ~s ~s", [App, Tag, Url]);
-format_source(App, {_, Url, Rev}) ->
- ?FMT("~p REV ~s ~s", [App, Rev, Url]);
-format_source(App, undefined) ->
- ?FMT("~p", [App]).
diff --git a/src/rebar_dia_compiler.erl b/src/rebar_dia_compiler.erl
deleted file mode 100644
index ba9d159..0000000
--- a/src/rebar_dia_compiler.erl
+++ /dev/null
@@ -1,106 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.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_dia_compiler).
-
--export([compile/2, clean/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
--spec compile(rebar_config:config(), file:filename()) -> 'ok'.
-compile(Config, _AppFile) ->
- rebar_base_compiler:run(Config, filelib:wildcard("dia/*.dia"),
- "dia", ".dia", "src", ".erl",
- fun compile_dia/3).
-
--spec clean(rebar_config:config(), file:filename()) -> 'ok'.
-clean(_Config, _AppFile) ->
- GeneratedFiles = dia_generated_files("dia", "src", "include"),
- ok = rebar_file_utils:delete_each(GeneratedFiles),
- ok.
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, compile) ->
- info_help("Build Diameter (*.dia) sources");
-info(help, clean) ->
- info_help("Delete generated Diameter files").
-
-info_help(Description) ->
- ?CONSOLE(
- "~s.~n"
- "~n"
- "Valid rebar.config options:~n"
- " {dia_opts, []} (see diameter_codegen:from_dict/4 documentation)~n",
- [Description]).
-
--spec compile_dia(file:filename(), file:filename(),
- rebar_config:config()) -> ok.
-compile_dia(Source, Target, Config) ->
- ok = filelib:ensure_dir(Target),
- ok = filelib:ensure_dir(filename:join("include", "dummy.hrl")),
- Opts = [{outdir, "src"}] ++ rebar_config:get(Config, dia_opts, []),
- case diameter_dict_util:parse({path, Source}, []) of
- {ok, Spec} ->
- FileName = dia_filename(Source, Spec),
- _ = diameter_codegen:from_dict(FileName, Spec, Opts, erl),
- _ = diameter_codegen:from_dict(FileName, Spec, Opts, hrl),
- HrlFile = filename:join("src", FileName ++ ".hrl"),
- case filelib:is_regular(HrlFile) of
- true ->
- ok = rebar_file_utils:mv(HrlFile, "include");
- false ->
- ok
- end;
- {error, Reason} ->
- ?ERROR("~s~n", [diameter_dict_util:format_error(Reason)])
- end.
-
-dia_generated_files(DiaDir, SrcDir, IncDir) ->
- F = fun(File, Acc) ->
- {ok, Spec} = diameter_dict_util:parse({path, File}, []),
- FileName = dia_filename(File, Spec),
- [filename:join([IncDir, FileName ++ ".hrl"]) |
- filelib:wildcard(filename:join([SrcDir, FileName ++ ".*"]))] ++ Acc
- end,
- lists:foldl(F, [], filelib:wildcard(filename:join([DiaDir, "*.dia"]))).
-
-dia_filename(File, Spec) ->
- case proplists:get_value(name, Spec) of
- undefined ->
- filename:rootname(filename:basename(File));
- Name ->
- Name
- end.
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index 6ae927d..ca670ac 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -91,8 +91,8 @@
%% 'old_inets'}]}.
%%
--spec compile(rebar_config:config(), file:filename()) -> 'ok'.
-compile(Config, _AppFile) ->
+-spec compile(rebar_config:config(), file:name()) -> 'ok'.
+compile(Config, Dir) ->
rebar_base_compiler:run(Config,
check_files(rebar_config:get_local(
Config, xrl_first_files, [])),
@@ -108,7 +108,7 @@ compile(Config, _AppFile) ->
Config, mib_first_files, [])),
"mibs", ".mib", "priv/mibs", ".bin",
fun compile_mib/3),
- doterl_compile(Config, "ebin").
+ doterl_compile(Config, Dir).
-spec clean(rebar_config:config(), file:filename()) -> 'ok'.
clean(Config, _AppFile) ->
@@ -159,10 +159,10 @@ test_compile(Config, Cmd, OutDir) ->
end, [], SrcDirs),
%% If it is not the first time rebar eunit or rebar qc is executed,
- %% there will be source files already present in OutDir. Since some
+ %% there will be source files already present in Dir. Since some
%% SCMs (like Perforce) set the source files as being read only (unless
%% they are checked out), we need to be sure that the files already
- %% present in OutDir are writable before doing the copy. This is done
+ %% present in Dir are writable before doing the copy. This is done
%% here by removing any file that was already present before calling
%% rebar_file_utils:cp_r.
@@ -290,17 +290,20 @@ is_lib_avail(Config, DictKey, Mod, Hrl, Name) ->
end.
-spec doterl_compile(rebar_config:config(), file:filename()) -> 'ok'.
-doterl_compile(Config, OutDir) ->
+doterl_compile(Config, Dir) ->
ErlOpts = rebar_utils:erl_opts(Config),
- doterl_compile(Config, OutDir, [], ErlOpts).
+ doterl_compile(Config, Dir, [], ErlOpts).
-doterl_compile(Config, OutDir, MoreSources, ErlOpts) ->
- ErlFirstFilesConf = rebar_config:get_list(Config, erl_first_files, []),
+doterl_compile(Config, Dir, MoreSources, ErlOpts) ->
+ OutDir = filename:join(Dir, "ebin"),
+ ErlFirstFilesConf = rebar_config:get_list(Config, erl_first_modules, []),
?DEBUG("erl_opts ~p~n", [ErlOpts]),
%% Support the src_dirs option allowing multiple directories to
%% contain erlang source. This might be used, for example, should
%% eunit tests be separated from the core application source.
- SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
+ SrcDirs = lists:map(fun(X) ->
+ filename:join(Dir, X)
+ end, rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts))),
AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources,
%% NOTE: If and when erl_first_files is not inherited anymore
%% (rebar_config:get_local instead of rebar_config:get_list), consider
@@ -309,12 +312,12 @@ doterl_compile(Config, OutDir, MoreSources, ErlOpts) ->
{ErlFirstFiles, RestErls} =
lists:partition(
fun(Source) ->
- lists:member(Source, ErlFirstFilesConf)
+ lists:member(list_to_atom(filename:basename(Source, ".erl")), ErlFirstFilesConf)
end, AllErlFiles),
%% Make sure that ebin/ exists and is on the path
- ok = filelib:ensure_dir(filename:join("ebin", "dummy.beam")),
+ ok = filelib:ensure_dir(filename:join([Dir, "ebin", "dummy.beam"])),
CurrPath = code:get_path(),
- true = code:add_path(filename:absname("ebin")),
+ true = code:add_path(filename:absname(filename:join(Dir, "ebin"))),
OutDir1 = proplists:get_value(outdir, ErlOpts, OutDir),
G = init_erlcinfo(Config, AllErlFiles),
%% Split RestErls so that files which are depended on are treated
@@ -353,7 +356,7 @@ doterl_compile(Config, OutDir, MoreSources, ErlOpts) ->
rebar_base_compiler:run(
Config, FirstErls, OtherErls,
fun(S, C) ->
- internal_erl_compile(C, S, OutDir1, ErlOpts, G)
+ internal_erl_compile(C, Dir, S, OutDir1, ErlOpts, G)
end),
true = code:set_path(CurrPath),
ok.
@@ -394,7 +397,8 @@ u_add_element(Elem, []) -> [Elem].
rebar_config:config()) -> [file:filename(), ...].
include_path(Source, Config) ->
ErlOpts = rebar_config:get(Config, erl_opts, []),
- lists:usort(["include", filename:dirname(Source)]
+ Dir = filename:join(lists:droplast(filename:split(filename:dirname(Source)))),
+ lists:usort([filename:join(Dir, "include"), filename:dirname(Source)]
++ proplists:get_all_values(i, ErlOpts)).
-spec needs_compile(file:filename(), file:filename(),
@@ -558,10 +562,10 @@ get_children(G, Source) ->
%% Return all files dependent on the Source.
digraph_utils:reaching_neighbours([Source], G).
--spec internal_erl_compile(rebar_config:config(), file:filename(),
+-spec internal_erl_compile(rebar_config:config(), file:filename(), file:filename(),
file:filename(), list(),
rebar_digraph()) -> 'ok' | 'skipped'.
-internal_erl_compile(Config, Source, OutDir, ErlOpts, G) ->
+internal_erl_compile(Config, Dir, Source, OutDir, ErlOpts, G) ->
%% Determine the target name and includes list by inspecting the source file
Module = filename:basename(Source, ".erl"),
Parents = get_parents(G, Source),
@@ -576,7 +580,7 @@ internal_erl_compile(Config, Source, OutDir, ErlOpts, G) ->
case needs_compile(Source, Target, Parents) of
true ->
Opts = [{outdir, filename:dirname(Target)}] ++
- ErlOpts ++ [{i, "include"}, return],
+ ErlOpts ++ [{i, filename:join(Dir, "include")}, return],
case compile:file(Source, Opts) of
{ok, _Mod} ->
ok;
diff --git a/src/rebar_erlydtl_compiler.erl b/src/rebar_erlydtl_compiler.erl
index 10387f5..fac8493 100644
--- a/src/rebar_erlydtl_compiler.erl
+++ b/src/rebar_erlydtl_compiler.erl
@@ -94,37 +94,56 @@
%% ]}.
-module(rebar_erlydtl_compiler).
--export([compile/2]).
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
%% for internal use only
-export([info/2]).
-include("rebar.hrl").
+-define(PROVIDER, erlydtl).
+-define(DEPS, [app_builder]).
+
%% ===================================================================
%% Public API
%% ===================================================================
-compile(Config, _AppFile) ->
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = compile,
+ bare = false,
+ deps = ?DEPS,
+ example = "compile",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+do(Config) ->
MultiDtlOpts = erlydtl_opts(Config),
OrigPath = code:get_path(),
true = code:add_path(rebar_utils:ebin_dir()),
Result = lists:foldl(fun(DtlOpts, _) ->
- rebar_base_compiler:run(Config, [],
- option(doc_root, DtlOpts),
- option(source_ext, DtlOpts),
- option(out_dir, DtlOpts),
- option(module_ext, DtlOpts) ++ ".beam",
- fun(S, T, C) ->
- compile_dtl(C, S, T, DtlOpts)
- end,
- [{check_last_mod, false},
- {recursive, option(recursive, DtlOpts)}])
- end, ok, MultiDtlOpts),
+ rebar_base_compiler:run(Config, [],
+ option(doc_root, DtlOpts),
+ option(source_ext, DtlOpts),
+ option(out_dir, DtlOpts),
+ option(module_ext, DtlOpts) ++ ".beam",
+ fun(S, T, C) ->
+ compile_dtl(C, S, T, DtlOpts)
+ end,
+ [{check_last_mod, false},
+ {recursive, option(recursive, DtlOpts)}])
+ end, ok, MultiDtlOpts),
true = code:set_path(OrigPath),
- Result.
+ {Result, Config}.
%% ===================================================================
%% Internal functions
diff --git a/src/rebar_escripter.erl b/src/rebar_escripter.erl
index 0cc43ef..395475f 100644
--- a/src/rebar_escripter.erl
+++ b/src/rebar_escripter.erl
@@ -26,6 +26,11 @@
%% -------------------------------------------------------------------
-module(rebar_escripter).
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
-export([escriptize/2,
clean/2]).
@@ -35,10 +40,33 @@
-include("rebar.hrl").
-include_lib("kernel/include/file.hrl").
+-define(PROVIDER, escriptize).
+-define(DEPS, [app_builder]).
+
%% ===================================================================
%% Public API
%% ===================================================================
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = escriptize,
+ bare = false,
+ deps = ?DEPS,
+ example = "escriptize",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ AppName = rebar_config:get_local(Config, escript_top_level_app, undefined),
+ App = rebar_config:get_app(Config, AppName),
+ {ok, Config1} = escriptize(Config, rebar_app_info:app_file(App)),
+ {ok, Config1}.
+
escriptize(Config0, AppFile) ->
%% Extract the application name from the archive -- this is the default
%% name of the generated script
diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl
new file mode 100644
index 0000000..6ed0f3f
--- /dev/null
+++ b/src/rebar_fetch.erl
@@ -0,0 +1,247 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% -------------------------------------------------------------------
+-module(rebar_fetch).
+
+-export([new/4,
+ current_ref/2,
+ download_source/2,
+ update_source1/2,
+ source_engine_avail/1,
+ source_engine_avail/2,
+ has_vcs_dir/2,
+ print_source/1,
+ format_source/2]).
+
+-include("rebar.hrl").
+
+-type dep_name() :: atom().
+-type dep_vsn() :: ec_semver:any_version().
+-type dep_source() :: {atom(), string(), ref()}.
+-type ref() :: string() | {atom(), string()}.
+
+-type dep() :: { dep_name(), dep_vsn(), dep_source() }.
+
+-record(p4_settings, {
+ client=undefined,
+ transport="tcp4:perforce:1666",
+ username,
+ password
+ }).
+
+new(Dir, App, Vsn, Source) ->
+ Ref = current_ref(Dir, Source),
+ {App, Vsn, setelement(3, Source, Ref)}.
+
+init_p4_settings(Basename) ->
+ #p4_settings{client =
+ case inet:gethostname() of
+ {ok,HostName} ->
+ HostName ++ "-"
+ ++ os:getenv("USER") ++ "-"
+ ++ Basename
+ ++ "-Rebar-automated-download"
+ end}.
+
+current_ref(AppDir, {git, _, _}) ->
+ string:strip(os:cmd("git --git-dir='" ++ AppDir ++ "/.git' rev-parse --verify HEAD"), both, $\n).
+
+download_source(AppDir, {p4, Url}) ->
+ download_source(AppDir, {p4, Url, "#head"});
+download_source(AppDir, {p4, Url, Rev}) ->
+ download_source(AppDir, {p4, Url, Rev, init_p4_settings(filename:basename(AppDir))});
+download_source(AppDir, {p4, Url, _Rev, Settings}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh_send("p4 client -i",
+ ?FMT("Client: ~s~n"
+ ++"Description: generated by Rebar~n"
+ ++"Root: ~s~n"
+ ++"View:~n"
+ ++" ~s/... //~s/...~n",
+ [Settings#p4_settings.client,
+ AppDir,
+ Url,
+ Settings#p4_settings.client]),
+ []),
+ rebar_utils:sh(?FMT("p4 -c ~s sync -f", [Settings#p4_settings.client]), []);
+download_source(AppDir, {hg, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("hg update ~s", [Rev]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url}) ->
+ download_source(AppDir, {git, Url, {branch, "HEAD"}});
+download_source(AppDir, {git, Url, ""}) ->
+ download_source(AppDir, {git, Url, {branch, "HEAD"}});
+download_source(AppDir, {git, Url, {branch, Branch}}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q origin/~s", [Branch]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url, {tag, Tag}}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), [{cd, AppDir}]);
+download_source(AppDir, {git, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, AppDir}]);
+download_source(AppDir, {bzr, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("bzr branch -r ~s ~s ~s",
+ [Rev, Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]);
+download_source(AppDir, {svn, Url, Rev}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("svn checkout -r ~s ~s ~s",
+ [Rev, Url, filename:basename(AppDir)]),
+ [{cd, filename:dirname(AppDir)}]);
+download_source(AppDir, {rsync, Url}) ->
+ ok = filelib:ensure_dir(AppDir),
+ rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s", [Url, AppDir]), []);
+download_source(AppDir, {fossil, Url}) ->
+ download_source(AppDir, {fossil, Url, ""});
+download_source(AppDir, {fossil, Url, Version}) ->
+ Repository = filename:join(AppDir, filename:basename(AppDir) ++ ".fossil"),
+ ok = filelib:ensure_dir(Repository),
+ ok = file:set_cwd(AppDir),
+ rebar_utils:sh(?FMT("fossil clone ~s ~s", [Url, Repository]),
+ [{cd, AppDir}]),
+ rebar_utils:sh(?FMT("fossil open ~s ~s --nested", [Repository, Version]),
+ []).
+
+update_source1(AppDir, Args) when element(1, Args) =:= p4 ->
+ download_source(AppDir, Args);
+update_source1(AppDir, {git, Url}) ->
+ update_source1(AppDir, {git, Url, {branch, "HEAD"}});
+update_source1(AppDir, {git, Url, ""}) ->
+ update_source1(AppDir, {git, Url, {branch, "HEAD"}});
+update_source1(AppDir, {git, _Url, {branch, Branch}}) ->
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Branch]), ShOpts),
+ rebar_utils:sh(
+ ?FMT("git pull --ff-only --no-rebase -q origin ~s", [Branch]),ShOpts);
+update_source1(AppDir, {git, _Url, {tag, Tag}}) ->
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Tag]), ShOpts);
+update_source1(AppDir, {git, _Url, Refspec}) ->
+ ShOpts = [{cd, AppDir}],
+ rebar_utils:sh("git fetch origin", ShOpts),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Refspec]), ShOpts);
+update_source1(AppDir, {svn, _Url, Rev}) ->
+ rebar_utils:sh(?FMT("svn up -r ~s", [Rev]), [{cd, AppDir}]);
+update_source1(AppDir, {hg, _Url, Rev}) ->
+ rebar_utils:sh(?FMT("hg pull -u -r ~s", [Rev]), [{cd, AppDir}]);
+update_source1(AppDir, {bzr, _Url, Rev}) ->
+ rebar_utils:sh(?FMT("bzr update -r ~s", [Rev]), [{cd, AppDir}]);
+update_source1(AppDir, {rsync, Url}) ->
+ rebar_utils:sh(?FMT("rsync -az --delete ~s/ ~s",[Url,AppDir]),[]);
+update_source1(AppDir, {fossil, Url}) ->
+ update_source1(AppDir, {fossil, Url, ""});
+update_source1(AppDir, {fossil, _Url, Version}) ->
+ ok = file:set_cwd(AppDir),
+ rebar_utils:sh("fossil pull", [{cd, AppDir}]),
+ rebar_utils:sh(?FMT("fossil update ~s", [Version]), []).
+
+%% ===================================================================
+%% Source helper functions
+%% ===================================================================
+
+source_engine_avail(Source) ->
+ Name = element(1, Source),
+ source_engine_avail(Name, Source).
+
+source_engine_avail(Name, Source)
+ when Name == hg; Name == git; Name == svn; Name == bzr; Name == rsync;
+ Name == fossil; Name == p4 ->
+ case vcs_client_vsn(Name) >= required_vcs_client_vsn(Name) of
+ true ->
+ true;
+ false ->
+ ?ABORT("Rebar requires version ~p or higher of ~s to process ~p\n",
+ [required_vcs_client_vsn(Name), Name, Source])
+ end.
+
+vcs_client_vsn(false, _VsnArg, _VsnRegex) ->
+ false;
+vcs_client_vsn(Path, VsnArg, VsnRegex) ->
+ {ok, Info} = rebar_utils:sh(Path ++ VsnArg, [{env, [{"LANG", "C"}]},
+ {use_stdout, false}]),
+ case re:run(Info, VsnRegex, [{capture, all_but_first, list}]) of
+ {match, Match} ->
+ list_to_tuple([list_to_integer(S) || S <- Match]);
+ _ ->
+ false
+ end.
+
+required_vcs_client_vsn(p4) -> {2013, 1};
+required_vcs_client_vsn(hg) -> {1, 1};
+required_vcs_client_vsn(git) -> {1, 5};
+required_vcs_client_vsn(bzr) -> {2, 0};
+required_vcs_client_vsn(svn) -> {1, 6};
+required_vcs_client_vsn(rsync) -> {2, 0};
+required_vcs_client_vsn(fossil) -> {1, 0}.
+
+vcs_client_vsn(p4) ->
+ vcs_client_vsn(rebar_utils:find_executable("p4"), " -V",
+ "Rev\\. .*/(\\d+)\\.(\\d)/");
+vcs_client_vsn(hg) ->
+ vcs_client_vsn(rebar_utils:find_executable("hg"), " --version",
+ "version (\\d+).(\\d+)");
+vcs_client_vsn(git) ->
+ vcs_client_vsn(rebar_utils:find_executable("git"), " --version",
+ "git version (\\d+).(\\d+)");
+vcs_client_vsn(bzr) ->
+ vcs_client_vsn(rebar_utils:find_executable("bzr"), " --version",
+ "Bazaar \\(bzr\\) (\\d+).(\\d+)");
+vcs_client_vsn(svn) ->
+ vcs_client_vsn(rebar_utils:find_executable("svn"), " --version",
+ "svn, version (\\d+).(\\d+)");
+vcs_client_vsn(rsync) ->
+ vcs_client_vsn(rebar_utils:find_executable("rsync"), " --version",
+ "rsync version (\\d+).(\\d+)");
+vcs_client_vsn(fossil) ->
+ vcs_client_vsn(rebar_utils:find_executable("fossil"), " version",
+ "version (\\d+).(\\d+)").
+
+has_vcs_dir(p4, _) ->
+ true;
+has_vcs_dir(git, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".git"));
+has_vcs_dir(hg, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".hg"));
+has_vcs_dir(bzr, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".bzr"));
+has_vcs_dir(svn, Dir) ->
+ filelib:is_dir(filename:join(Dir, ".svn"))
+ orelse filelib:is_dir(filename:join(Dir, "_svn"));
+has_vcs_dir(rsync, _) ->
+ true;
+has_vcs_dir(_, _) ->
+ true.
+
+print_source({App, _, Source}) ->
+ ?CONSOLE("~s~n", [format_source(App, Source)]).
+
+format_source(App, {p4, Url}) ->
+ format_source(App, {p4, Url, "#head"});
+format_source(App, {git, Url}) ->
+ ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
+format_source(App, {git, Url, ""}) ->
+ ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
+format_source(App, {git, Url, {branch, Branch}}) ->
+ ?FMT("~p BRANCH ~s ~s", [App, Branch, Url]);
+format_source(App, {git, Url, {tag, Tag}}) ->
+ ?FMT("~p TAG ~s ~s", [App, Tag, Url]);
+format_source(App, {_, Url, Rev}) ->
+ ?FMT("~p REV ~s ~s", [App, Rev, Url]);
+format_source(App, undefined) ->
+ ?FMT("~p", [App]).
diff --git a/src/rebar_lfe_compiler.erl b/src/rebar_lfe_compiler.erl
deleted file mode 100644
index 8488b0f..0000000
--- a/src/rebar_lfe_compiler.erl
+++ /dev/null
@@ -1,84 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com),
-%% Tim Dysinger (tim@dysinger.net)
-%%
-%% 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_lfe_compiler).
-
--export([compile/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
-compile(Config, _AppFile) ->
- FirstFiles = rebar_config:get_list(Config, lfe_first_files, []),
- rebar_base_compiler:run(Config, FirstFiles, "src", ".lfe", "ebin", ".beam",
- fun compile_lfe/3).
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, compile) ->
- ?CONSOLE(
- "Build Lisp Flavoured Erlang (*.lfe) sources.~n"
- "~n"
- "Valid rebar.config options:~n"
- " erl_opts is reused.'~n",
- []).
-
-compile_lfe(Source, _Target, Config) ->
- case code:which(lfe_comp) of
- non_existing ->
- ?ERROR("~n"
- "*** MISSING LFE COMPILER ***~n"
- " You must do one of the following:~n"
- " a) Install LFE globally in your erl libs~n"
- " b) Add LFE as a dep for your project, eg:~n"
- " {lfe, \"0.6.1\",~n"
- " {git, \"git://github.com/rvirding/lfe\",~n"
- " {tag, \"v0.6.1\"}}}~n"
- "~n", []),
- ?FAIL;
- _ ->
- ErlOpts = rebar_utils:erl_opts(Config),
- Opts = [{i, "include"}, {outdir, "ebin"}, return] ++ ErlOpts,
- case lfe_comp:file(Source, Opts) of
- {ok, _Mod, Ws} ->
- rebar_base_compiler:ok_tuple(Config, Source, Ws);
- {error, Es, Ws} ->
- rebar_base_compiler:error_tuple(Config, Source,
- Es, Ws, Opts);
- _ ->
- ?FAIL
- end
- end.
diff --git a/src/rebar_metacmds.erl b/src/rebar_metacmds.erl
deleted file mode 100644
index 6e223bd..0000000
--- a/src/rebar_metacmds.erl
+++ /dev/null
@@ -1,56 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2013-2014 Tuncer Ayaz
-%%
-%% 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_metacmds).
-
--export(['prepare-deps'/2,
- 'refresh-deps'/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-'prepare-deps'(Config, _AppFile) ->
- rebar:run(enable_recursion(Config), ["get-deps", "compile"]).
-
-'refresh-deps'(Config, _AppFile) ->
- rebar:run(enable_recursion(Config), ["update-deps", "compile"]).
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, 'prepare-deps') ->
- ?CONSOLE("Meta command to run 'rebar -r get-deps compile'.~n", []);
-info(help, 'refresh-deps') ->
- ?CONSOLE("Meta command to run 'rebar -r update-deps compile'.~n", []).
-
-enable_recursion(Config) ->
- rebar_config:set_xconf(Config, recursive, true).
diff --git a/src/rebar_mustache.erl b/src/rebar_mustache.erl
deleted file mode 100644
index 9016c0f..0000000
--- a/src/rebar_mustache.erl
+++ /dev/null
@@ -1,230 +0,0 @@
-%% The MIT License
-%%
-%% Copyright (c) 2009 Tom Preston-Werner <tom@mojombo.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.
-
-%% See the README at http://github.com/mojombo/mustache.erl for additional
-%% documentation and usage examples.
-
--module(rebar_mustache). %% v0.1.0
--author("Tom Preston-Werner").
--export([compile/1, compile/2, render/1, render/2, render/3, get/2, get/3, escape/1, start/1]).
-
--record(mstate, {mod = undefined,
- section_re = undefined,
- tag_re = undefined}).
-
--define(MUSTACHE_STR, "rebar_mustache").
-
-compile(Body) when is_list(Body) ->
- State = #mstate{},
- CompiledTemplate = pre_compile(Body, State),
- % io:format("~p~n~n", [CompiledTemplate]),
- % io:format(CompiledTemplate ++ "~n", []),
- {ok, Tokens, _} = erl_scan:string(CompiledTemplate),
- {ok, [Form]} = erl_parse:parse_exprs(Tokens),
- Bindings = erl_eval:new_bindings(),
- {value, Fun, _} = erl_eval:expr(Form, Bindings),
- Fun;
-compile(Mod) ->
- TemplatePath = template_path(Mod),
- compile(Mod, TemplatePath).
-
-compile(Mod, File) ->
- code:purge(Mod),
- {module, _} = code:load_file(Mod),
- {ok, TemplateBin} = file:read_file(File),
- Template = re:replace(TemplateBin, "\"", "\\\\\"", [global, {return,list}]),
- State = #mstate{mod = Mod},
- CompiledTemplate = pre_compile(Template, State),
- % io:format("~p~n~n", [CompiledTemplate]),
- % io:format(CompiledTemplate ++ "~n", []),
- {ok, Tokens, _} = erl_scan:string(CompiledTemplate),
- {ok, [Form]} = erl_parse:parse_exprs(Tokens),
- Bindings = erl_eval:new_bindings(),
- {value, Fun, _} = erl_eval:expr(Form, Bindings),
- Fun.
-
-render(Mod) ->
- TemplatePath = template_path(Mod),
- render(Mod, TemplatePath).
-
-render(Body, Ctx) when is_list(Body) ->
- TFun = compile(Body),
- render(undefined, TFun, Ctx);
-render(Mod, File) when is_list(File) ->
- render(Mod, File, dict:new());
-render(Mod, CompiledTemplate) ->
- render(Mod, CompiledTemplate, dict:new()).
-
-render(Mod, File, Ctx) when is_list(File) ->
- CompiledTemplate = compile(Mod, File),
- render(Mod, CompiledTemplate, Ctx);
-render(Mod, CompiledTemplate, Ctx) ->
- Ctx2 = dict:store('__mod__', Mod, Ctx),
- lists:flatten(CompiledTemplate(Ctx2)).
-
-pre_compile(T, State) ->
- SectionRE = "\{\{\#([^\}]*)}}\s*(.+?){{\/\\1\}\}\s*",
- {ok, CompiledSectionRE} = re:compile(SectionRE, [dotall]),
- TagRE = "\{\{(#|=|!|<|>|\{)?(.+?)\\1?\}\}+",
- {ok, CompiledTagRE} = re:compile(TagRE, [dotall]),
- State2 = State#mstate{section_re = CompiledSectionRE, tag_re = CompiledTagRE},
- "fun(Ctx) -> " ++
- "CFun = fun(A, B) -> A end, " ++
- compiler(T, State2) ++ " end.".
-
-compiler(T, State) ->
- Res = re:run(T, State#mstate.section_re),
- case Res of
- {match, [{M0, M1}, {N0, N1}, {C0, C1}]} ->
- Front = string:substr(T, 1, M0),
- Back = string:substr(T, M0 + M1 + 1),
- Name = string:substr(T, N0 + 1, N1),
- Content = string:substr(T, C0 + 1, C1),
- "[" ++ compile_tags(Front, State) ++
- " | [" ++ compile_section(Name, Content, State) ++
- " | [" ++ compiler(Back, State) ++ "]]]";
- nomatch ->
- compile_tags(T, State)
- end.
-
-compile_section(Name, Content, State) ->
- Mod = State#mstate.mod,
- Result = compiler(Content, State),
- "fun() -> " ++
- "case " ++ ?MUSTACHE_STR ++ ":get(" ++ Name ++ ", Ctx, " ++ atom_to_list(Mod) ++ ") of " ++
- "\"true\" -> " ++
- Result ++ "; " ++
- "\"false\" -> " ++
- "[]; " ++
- "List when is_list(List) -> " ++
- "[fun(Ctx) -> " ++ Result ++ " end(dict:merge(CFun, SubCtx, Ctx)) || SubCtx <- List]; " ++
- "Else -> " ++
- "throw({template, io_lib:format(\"Bad context for ~p: ~p\", [" ++ Name ++ ", Else])}) " ++
- "end " ++
- "end()".
-
-compile_tags(T, State) ->
- Res = re:run(T, State#mstate.tag_re),
- case Res of
- {match, [{M0, M1}, K, {C0, C1}]} ->
- Front = string:substr(T, 1, M0),
- Back = string:substr(T, M0 + M1 + 1),
- Content = string:substr(T, C0 + 1, C1),
- Kind = tag_kind(T, K),
- Result = compile_tag(Kind, Content, State),
- "[\"" ++ Front ++
- "\" | [" ++ Result ++
- " | " ++ compile_tags(Back, State) ++ "]]";
- nomatch ->
- "[\"" ++ T ++ "\"]"
- end.
-
-tag_kind(_T, {-1, 0}) ->
- none;
-tag_kind(T, {K0, K1}) ->
- string:substr(T, K0 + 1, K1).
-
-compile_tag(none, Content, State) ->
- Mod = State#mstate.mod,
- ?MUSTACHE_STR ++ ":escape(" ++ ?MUSTACHE_STR ++ ":get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ "))";
-compile_tag("{", Content, State) ->
- Mod = State#mstate.mod,
- ?MUSTACHE_STR ++ ":get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ ")";
-compile_tag("!", _Content, _State) ->
- "[]".
-
-template_dir(Mod) ->
- DefaultDirPath = filename:dirname(code:which(Mod)),
- case application:get_env(mustache, templates_dir) of
- {ok, DirPath} when is_list(DirPath) ->
- case filelib:ensure_dir(DirPath) of
- ok -> DirPath;
- _ -> DefaultDirPath
- end;
- _ ->
- DefaultDirPath
- end.
-template_path(Mod) ->
- DirPath = template_dir(Mod),
- Basename = atom_to_list(Mod),
- filename:join(DirPath, Basename ++ ".mustache").
-
-get(Key, Ctx) when is_list(Key) ->
- {ok, Mod} = dict:find('__mod__', Ctx),
- get(list_to_atom(Key), Ctx, Mod);
-get(Key, Ctx) ->
- {ok, Mod} = dict:find('__mod__', Ctx),
- get(Key, Ctx, Mod).
-
-get(Key, Ctx, Mod) when is_list(Key) ->
- get(list_to_atom(Key), Ctx, Mod);
-get(Key, Ctx, Mod) ->
- case dict:find(Key, Ctx) of
- {ok, Val} ->
- % io:format("From Ctx {~p, ~p}~n", [Key, Val]),
- to_s(Val);
- error ->
- case erlang:function_exported(Mod, Key, 1) of
- true ->
- Val = to_s(Mod:Key(Ctx)),
- % io:format("From Mod/1 {~p, ~p}~n", [Key, Val]),
- Val;
- false ->
- case erlang:function_exported(Mod, Key, 0) of
- true ->
- Val = to_s(Mod:Key()),
- % io:format("From Mod/0 {~p, ~p}~n", [Key, Val]),
- Val;
- false ->
- []
- end
- end
- end.
-
-to_s(Val) when is_integer(Val) ->
- integer_to_list(Val);
-to_s(Val) when is_float(Val) ->
- io_lib:format("~.2f", [Val]);
-to_s(Val) when is_atom(Val) ->
- atom_to_list(Val);
-to_s(Val) ->
- Val.
-
-escape(HTML) ->
- escape(HTML, []).
-
-escape([], Acc) ->
- lists:reverse(Acc);
-escape(["<" | Rest], Acc) ->
- escape(Rest, lists:reverse("&lt;", Acc));
-escape([">" | Rest], Acc) ->
- escape(Rest, lists:reverse("&gt;", Acc));
-escape(["&" | Rest], Acc) ->
- escape(Rest, lists:reverse("&amp;", Acc));
-escape([X | Rest], Acc) ->
- escape(Rest, [X | Acc]).
-
-%%---------------------------------------------------------------------------
-
-start([T]) ->
- Out = render(list_to_atom(T)),
- io:format(Out ++ "~n", []).
diff --git a/src/rebar_neotoma_compiler.erl b/src/rebar_neotoma_compiler.erl
deleted file mode 100644
index 5549dc4..0000000
--- a/src/rebar_neotoma_compiler.erl
+++ /dev/null
@@ -1,163 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2010 Cliff Moon (cliff@moonpolysoft.com)
-%%
-%% Permission is hereby granted, free of charge, to any person obtaining a copy
-%% of this software and associated documentation files (the "Software"), to deal
-%% in the Software without restriction, including without limitation the rights
-%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-%% copies of the Software, and to permit persons to whom the Software is
-%% furnished to do so, subject to the following conditions:
-%%
-%% The above copyright notice and this permission notice shall be included in
-%% all copies or substantial portions of the Software.
-%%
-%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-%% THE SOFTWARE.
-%% -------------------------------------------------------------------
-
-%% The rebar_neotoma module is a plugin for rebar that compiles
-%% neotoma peg files. By default, it compiles all src/*.peg to src/*.erl
-%%
-%% Configuration options should be placed in rebar.config under
-%% neotoma_opts. Available options include:
-%%
-%% doc_root: where to find the peg files to compile.
-%% "src" by default
-%% out_dir: where to put the generated erl files.
-%% "src" by defualt
-%% module_ext: characters to append to the module's name.
-%% "" by default
-%% source_ext: extension of peg source files
--module(rebar_neotoma_compiler).
-
--export([compile/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ============================================================================
-%% Public API
-%% ============================================================================
-
-compile(Config, _AppFile) ->
- NeoOpts = neotoma_opts(Config),
- rebar_base_compiler:run(Config, [],
- option(doc_root, NeoOpts), ".peg",
- option(out_dir, NeoOpts),
- option(module_ext, NeoOpts) ++ ".erl",
- fun compile_neo/3, [{check_last_mod, true}]).
-
-%% ============================================================================
-%% Internal functions
-%% ============================================================================
-
-info(help, compile) ->
- ?CONSOLE(
- "Build Neotoma (*.peg) sources.~n"
- "~n"
- "Valid rebar.config options:~n"
- " ~p~n",
- [
- {neotoma_opts, [{doc_root, "src"},
- {out_dir, "src"},
- {source_ext, ".peg"},
- {module_ext, ""}]}
- ]).
-
-neotoma_opts(Config) ->
- rebar_config:get(Config, neotoma_opts, []).
-
-option(Opt, Options) ->
- proplists:get_value(Opt, Options, default(Opt)).
-
-default(doc_root) -> "src";
-default(out_dir) -> "src";
-default(module_ext) -> "";
-default(source_ext) -> ".peg".
-
-compile_neo(Source, Target, Config) ->
- case code:which(neotoma) of
- non_existing ->
- ?ERROR("~n===============================================~n"
- " You need to install neotoma to compile PEG grammars~n"
- " Download the latest tarball release from github~n"
- " https://github.com/seancribbs/neotoma~n"
- " and install it into your erlang library dir~n"
- "===============================================~n~n", []),
- ?FAIL;
- _ ->
- case needs_compile(Source, Target, Config) of
- true ->
- do_compile(Source, Target, Config);
- false ->
- skipped
- end
- end.
-
-do_compile(Source, _Target, Config) ->
- %% TODO: Check last mod on target and referenced DTLs here..
- NeoOpts = neotoma_opts(Config),
- %% ensure that doc_root and out_dir are defined,
- %% using defaults if necessary
- Opts = [{output, option(out_dir, NeoOpts)},
- {module, list_to_atom(filename:basename(Source, ".peg")
- ++ option(module_ext, NeoOpts))}],
- case neotoma:file(Source, Opts ++ NeoOpts) of
- ok ->
- ok;
- Reason ->
- ?ERROR("Compiling peg ~s failed:~n ~p~n",
- [Source, Reason]),
- ?FAIL
- end.
-
-needs_compile(Source, Target, Config) ->
- LM = filelib:last_modified(Target),
- LM < filelib:last_modified(Source) orelse
- lists:any(fun(D) -> LM < filelib:last_modified(D) end,
- referenced_pegs(Source, Config)).
-
-referenced_pegs(Source, Config) ->
- Set = referenced_pegs1([Source], Config,
- sets:add_element(Source, sets:new())),
- sets:to_list(sets:del_element(Source, Set)).
-
-referenced_pegs1(Step, Config, Seen) ->
- NeoOpts = neotoma_opts(Config),
- ExtMatch = re:replace(option(source_ext, NeoOpts), "\.", "\\\\\\\\.",
- [{return, list}]),
-
- ShOpts = [{use_stdout, false}, return_on_error],
- AllRefs =
- lists:append(
- [begin
- Cmd = lists:flatten(["grep -o [^\\\"]*",
- ExtMatch, " ", F]),
- case rebar_utils:sh(Cmd, ShOpts) of
- {ok, Res} ->
- string:tokens(Res, "\n");
- {error, _} ->
- ""
- end
- end || F <- Step]),
- DocRoot = option(doc_root, NeoOpts),
- WithPaths = [ filename:join([DocRoot, F]) || F <- AllRefs ],
- Existing = [F || F <- WithPaths, filelib:is_regular(F)],
- New = sets:subtract(sets:from_list(Existing), Seen),
- case sets:size(New) of
- 0 -> Seen;
- _ -> referenced_pegs1(sets:to_list(New), Config,
- sets:union(New, Seen))
- end.
diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl
index b3566c8..e390f3b 100644
--- a/src/rebar_otp_app.erl
+++ b/src/rebar_otp_app.erl
@@ -38,31 +38,33 @@
%% Public API
%% ===================================================================
-compile(Config, File) ->
+compile(Config, App) ->
%% 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.
- {Config1, AppFile} = case rebar_app_utils:is_app_src(File) of
- true ->
- preprocess(Config, File);
- false ->
- {Config, File}
- end,
+ Dir = rebar_app_info:dir(App),
+ {Config2, App1} = case rebar_app_info:app_file_src(App) of
+ undefined ->
+ {Config, App};
+ AppFileSrc ->
+ {Config1, File} = preprocess(Config, Dir, AppFileSrc),
+ {Config1, rebar_app_info:app_file(App, File)}
+ end,
%% Load the app file and validate it.
- case rebar_app_utils:load_app_file(Config1, AppFile) of
- {ok, Config2, AppName, AppData} ->
+ AppFile = rebar_app_info:app_file(App1),
+ case rebar_app_utils:load_app_file(Config2, AppFile) of
+ {ok, Config3, 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(Config1, validate_app_modules, true) of
+ case rebar_config:get_local(Config3, validate_app_modules, true) of
true ->
Modules = proplists:get_value(modules, AppData),
- {validate_modules(AppName, Modules), Config2};
+ {validate_modules(Dir, AppName, Modules), App1};
false ->
- {ok, Config2}
+ {ok, App1}
end;
{error, Reason} ->
?ABORT("Failed to load app file ~s: ~p\n", [AppFile, Reason])
@@ -105,13 +107,13 @@ info_help(Description) ->
{validate_app_modules, true}
]).
-preprocess(Config, AppSrcFile) ->
+preprocess(Config, Dir, AppSrcFile) ->
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(Config1) ++ [{modules, ebin_modules()}],
+ AppVars = load_app_vars(Config1) ++ [{modules, ebin_modules(Dir)}],
A1 = apply_app_vars(AppVars, AppData),
@@ -171,15 +173,15 @@ validate_name(AppName, File) ->
?FAIL
end.
-validate_modules(AppName, undefined) ->
+validate_modules(_Dir, AppName, undefined) ->
?ERROR("Missing modules declaration in ~p.app~n", [AppName]),
?FAIL;
-validate_modules(AppName, Mods) ->
+validate_modules(Dir, AppName, Mods) ->
%% Construct two sets -- one for the actual .beam files in ebin/
%% and one for the modules
%% listed in the .app file
- EbinSet = ordsets:from_list(ebin_modules()),
+ EbinSet = ordsets:from_list(ebin_modules(Dir)),
ModSet = ordsets:from_list(Mods),
%% Identify .beam files listed in the .app, but not present in ebin/
@@ -206,9 +208,9 @@ validate_modules(AppName, Mods) ->
?FAIL
end.
-ebin_modules() ->
- lists:sort([rebar_utils:beam_to_mod("ebin", N) ||
- N <- rebar_utils:beams("ebin")]).
+ebin_modules(Dir) ->
+ lists:sort([rebar_utils:beam_to_mod(N) ||
+ N <- rebar_utils:beams(filename:join(Dir, "ebin"))]).
ensure_registered(AppData) ->
case lists:keyfind(registered, 1, AppData) of
diff --git a/src/rebar_port_compiler.erl b/src/rebar_port_compiler.erl
deleted file mode 100644
index 35adc3c..0000000
--- a/src/rebar_port_compiler.erl
+++ /dev/null
@@ -1,617 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
-%%
-%% 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_port_compiler).
-
--export([compile/2,
- clean/2]).
-
-%% for internal use only
--export([setup_env/1,
- info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
-%% Supported configuration variables:
-%%
-%% * port_specs - Erlang list of tuples of the forms
-%% {ArchRegex, TargetFile, Sources, Options}
-%% {ArchRegex, TargetFile, Sources}
-%% {TargetFile, Sources}
-%%
-%% * port_env - Erlang list of key/value pairs which will control
-%% the environment when running the compiler and linker.
-%%
-%% By default, the following variables are defined:
-%% CC - C compiler
-%% CXX - C++ compiler
-%% CFLAGS - C compiler
-%% CXXFLAGS - C++ compiler
-%% LDFLAGS - Link flags
-%% ERL_CFLAGS - default -I paths for erts and ei
-%% ERL_LDFLAGS - default -L and -lerl_interface -lei
-%% DRV_CFLAGS - flags that will be used for compiling
-%% DRV_LDFLAGS - flags that will be used for linking
-%% EXE_CFLAGS - flags that will be used for compiling
-%% EXE_LDFLAGS - flags that will be used for linking
-%% ERL_EI_LIBDIR - ei library directory
-%% DRV_CXX_TEMPLATE - C++ command template
-%% DRV_CC_TEMPLATE - C command template
-%% DRV_LINK_TEMPLATE - Linker command template
-%% EXE_CXX_TEMPLATE - C++ command template
-%% EXE_CC_TEMPLATE - C command template
-%% EXE_LINK_TEMPLATE - Linker command template
-%% PORT_IN_FILES - contains a space separated list of input
-%% file(s), (used in command template)
-%% PORT_OUT_FILE - contains the output filename (used in
-%% command template)
-%%
-%% Note that if you wish to extend (vs. replace) these variables,
-%% you MUST include a shell-style reference in your definition.
-%% e.g. to extend CFLAGS, do something like:
-%%
-%% {port_env, [{"CFLAGS", "$CFLAGS -MyOtherOptions"}]}
-%%
-%% It is also possible to specify platform specific options
-%% by specifying a triplet where the first string is a regex
-%% that is checked against Erlang's system architecture string.
-%% e.g. to specify a CFLAG that only applies to x86_64 on linux
-%% do:
-%%
-%% {port_env, [{"x86_64.*-linux", "CFLAGS",
-%% "$CFLAGS -X86Options"}]}
-%%
-
--record(spec, {type::'drv' | 'exe',
- target::file:filename(),
- sources = [] :: [file:filename(), ...],
- objects = [] :: [file:filename(), ...],
- opts = [] ::list() | []}).
-
-compile(Config, AppFile) ->
- case get_specs(Config, AppFile) of
- [] ->
- ok;
- Specs ->
- SharedEnv = rebar_config:get_env(Config, rebar_deps) ++
- rebar_config:get_env(Config, ?MODULE),
-
- %% Compile each of the sources
- NewBins = compile_sources(Config, Specs, SharedEnv),
-
- %% Make sure that the target directories exist
- ?INFO("Using specs ~p\n", [Specs]),
- lists:foreach(fun(#spec{target=Target}) ->
- ok = filelib:ensure_dir(Target)
- end, Specs),
-
- %% Only relink if necessary, given the Target
- %% and list of new binaries
- lists:foreach(
- fun(#spec{target=Target, objects=Bins, opts=Opts}) ->
- AllBins = [sets:from_list(Bins),
- sets:from_list(NewBins)],
- Intersection = sets:intersection(AllBins),
- case needs_link(Target, sets:to_list(Intersection)) of
- true ->
- LinkTemplate = select_link_template(Target),
- Env = proplists:get_value(env, Opts, SharedEnv),
- Cmd = expand_command(LinkTemplate, Env,
- string:join(Bins, " "),
- Target),
- rebar_utils:sh(Cmd, [{env, Env}]);
- false ->
- ?INFO("Skipping relink of ~s\n", [Target]),
- ok
- end
- end, Specs)
- end.
-
-clean(Config, AppFile) ->
- case get_specs(Config, AppFile) of
- [] ->
- ok;
- Specs ->
- lists:foreach(fun(#spec{target=Target, objects=Objects}) ->
- rebar_file_utils:delete_each([Target]),
- rebar_file_utils:delete_each(Objects)
- end, Specs)
- end,
- ok.
-
-setup_env(Config) ->
- setup_env(Config, []).
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, compile) ->
- info_help("Build port sources");
-info(help, clean) ->
- info_help("Delete port build results").
-
-info_help(Description) ->
- ?CONSOLE(
- "~s.~n"
- "~n"
- "Valid rebar.config options:~n"
- " ~p~n"
- " ~p~n",
- [
- Description,
- {port_env, [{"CFLAGS", "$CFLAGS -Ifoo"},
- {"freebsd", "LDFLAGS", "$LDFLAGS -lfoo"}]},
- {port_specs, [{"priv/so_name.so", ["c_src/*.c"]},
- {"linux", "priv/hello_linux", ["c_src/hello_linux.c"]},
- {"linux", "priv/hello_linux", ["c_src/*.c"], [{env, []}]}]}
- ]).
-
-setup_env(Config, ExtraEnv) ->
- %% Extract environment values from the config (if specified) and
- %% merge with the default for this operating system. This enables
- %% max flexibility for users.
- DefaultEnv = filter_env(default_env(), []),
-
- %% Get any port-specific envs; use port_env first and then fallback
- %% to port_envs for compatibility
- RawPortEnv = rebar_config:get_list(Config, port_env,
- rebar_config:get_list(Config, port_envs, [])),
-
- PortEnv = filter_env(RawPortEnv, []),
- Defines = get_defines(Config),
- OverrideEnv = Defines ++ PortEnv ++ filter_env(ExtraEnv, []),
- RawEnv = apply_defaults(os_env(), DefaultEnv) ++ OverrideEnv,
- expand_vars_loop(merge_each_var(RawEnv, [])).
-
-get_defines(Config) ->
- RawDefines = rebar_config:get_xconf(Config, defines, []),
- Defines = string:join(["-D" ++ D || D <- RawDefines], " "),
- [{"ERL_CFLAGS", "$ERL_CFLAGS " ++ Defines}].
-
-replace_extension(File, NewExt) ->
- OldExt = filename:extension(File),
- replace_extension(File, OldExt, NewExt).
-
-replace_extension(File, OldExt, NewExt) ->
- filename:rootname(File, OldExt) ++ NewExt.
-
-%%
-%% == compile and link ==
-%%
-
-compile_sources(Config, Specs, SharedEnv) ->
- lists:foldl(
- fun(#spec{sources=Sources, type=Type, opts=Opts}, NewBins) ->
- Env = proplists:get_value(env, Opts, SharedEnv),
- compile_each(Config, Sources, Type, Env, NewBins)
- end, [], Specs).
-
-compile_each(_Config, [], _Type, _Env, NewBins) ->
- lists:reverse(NewBins);
-compile_each(Config, [Source | Rest], Type, Env, NewBins) ->
- Ext = filename:extension(Source),
- Bin = replace_extension(Source, Ext, ".o"),
- case needs_compile(Source, Bin) of
- true ->
- Template = select_compile_template(Type, compiler(Ext)),
- Cmd = expand_command(Template, Env, Source, Bin),
- ShOpts = [{env, Env}, return_on_error, {use_stdout, false}],
- exec_compiler(Config, Source, Cmd, ShOpts),
- compile_each(Config, Rest, Type, Env, [Bin | NewBins]);
- false ->
- ?INFO("Skipping ~s\n", [Source]),
- compile_each(Config, Rest, Type, Env, NewBins)
- end.
-
-exec_compiler(Config, Source, Cmd, ShOpts) ->
- case rebar_utils:sh(Cmd, ShOpts) of
- {error, {_RC, RawError}} ->
- AbsSource = case rebar_utils:processing_base_dir(Config) of
- true ->
- Source;
- false ->
- filename:absname(Source)
- end,
- ?CONSOLE("Compiling ~s\n", [AbsSource]),
- Error = re:replace(RawError, Source, AbsSource,
- [{return, list}, global]),
- ?CONSOLE("~s", [Error]),
- ?FAIL;
- {ok, Output} ->
- ?CONSOLE("Compiling ~s\n", [Source]),
- ?CONSOLE("~s", [Output])
- end.
-
-needs_compile(Source, Bin) ->
- %% TODO: Generate depends using gcc -MM so we can also
- %% check for include changes
- filelib:last_modified(Bin) < filelib:last_modified(Source).
-
-needs_link(SoName, []) ->
- filelib:last_modified(SoName) == 0;
-needs_link(SoName, NewBins) ->
- MaxLastMod = lists:max([filelib:last_modified(B) || B <- NewBins]),
- case filelib:last_modified(SoName) of
- 0 ->
- ?DEBUG("Last mod is 0 on ~s\n", [SoName]),
- true;
- Other ->
- ?DEBUG("Checking ~p >= ~p\n", [MaxLastMod, Other]),
- MaxLastMod >= Other
- end.
-
-%%
-%% == port_specs ==
-%%
-
-get_specs(Config, AppFile) ->
- Specs = case rebar_config:get_local(Config, port_specs, []) of
- [] ->
- %% No spec provided. Construct a spec
- %% from old-school so_name and sources
- [port_spec_from_legacy(Config, AppFile)];
- PortSpecs ->
- Filtered = filter_port_specs(PortSpecs),
- OsType = os:type(),
- [get_port_spec(Config, OsType, Spec) || Spec <- Filtered]
- end,
- [S || S <- Specs, S#spec.sources /= []].
-
-port_spec_from_legacy(Config, AppFile) ->
- %% Get the target from the so_name variable
- Target = case rebar_config:get(Config, so_name, undefined) of
- undefined ->
- %% Generate a sensible default from app file
- {_, AppName} = rebar_app_utils:app_name(Config, AppFile),
- filename:join("priv",
- lists:concat([AppName, "_drv.so"]));
- AName ->
- %% Old form is available -- use it
- filename:join("priv", AName)
- end,
- %% Get the list of source files from port_sources
- Sources = port_sources(rebar_config:get_list(Config, port_sources,
- ["c_src/*.c"])),
- #spec { type = target_type(Target),
- target = maybe_switch_extension(os:type(), Target),
- sources = Sources,
- objects = port_objects(Sources) }.
-
-filter_port_specs(Specs) ->
- [S || S <- Specs, filter_port_spec(S)].
-
-filter_port_spec({ArchRegex, _, _, _}) ->
- rebar_utils:is_arch(ArchRegex);
-filter_port_spec({ArchRegex, _, _}) ->
- rebar_utils:is_arch(ArchRegex);
-filter_port_spec({_, _}) ->
- true.
-
-get_port_spec(Config, OsType, {Target, Sources}) ->
- get_port_spec(Config, OsType, {undefined, Target, Sources, []});
-get_port_spec(Config, OsType, {Arch, Target, Sources}) ->
- get_port_spec(Config, OsType, {Arch, Target, Sources, []});
-get_port_spec(Config, OsType, {_Arch, Target, Sources, Opts}) ->
- SourceFiles = port_sources(Sources),
- ObjectFiles = port_objects(SourceFiles),
- #spec{type=target_type(Target),
- target=maybe_switch_extension(OsType, Target),
- sources=SourceFiles,
- objects=ObjectFiles,
- opts=port_opts(Config, Opts)}.
-
-port_sources(Sources) ->
- lists:flatmap(fun filelib:wildcard/1, Sources).
-
-port_objects(SourceFiles) ->
- [replace_extension(O, ".o") || O <- SourceFiles].
-
-port_opts(Config, Opts) ->
- [port_opt(Config, O) || O <- Opts].
-
-port_opt(Config, {env, Env}) ->
- {env, setup_env(Config, Env)};
-port_opt(_Config, Opt) ->
- Opt.
-
-maybe_switch_extension({win32, nt}, Target) ->
- switch_to_dll_or_exe(Target);
-maybe_switch_extension(_OsType, Target) ->
- Target.
-
-switch_to_dll_or_exe(Target) ->
- case filename:extension(Target) of
- ".so" -> filename:rootname(Target, ".so") ++ ".dll";
- [] -> Target ++ ".exe";
- _Other -> Target
- end.
-
-%%
-%% == port_env ==
-%%
-
-%%
-%% Choose a compiler variable, based on a provided extension
-%%
-compiler(".cc") -> "$CXX";
-compiler(".cp") -> "$CXX";
-compiler(".cxx") -> "$CXX";
-compiler(".cpp") -> "$CXX";
-compiler(".CPP") -> "$CXX";
-compiler(".c++") -> "$CXX";
-compiler(".C") -> "$CXX";
-compiler(_) -> "$CC".
-
-%%
-%% Given a list of {Key, Value} variables, and another list of default
-%% {Key, Value} variables, return a merged list where the rule is if the
-%% default is expandable expand it with the value of the variable list,
-%% otherwise just return the value of the variable.
-%%
-apply_defaults(Vars, Defaults) ->
- dict:to_list(
- dict:merge(fun(Key, VarValue, DefaultValue) ->
- case is_expandable(DefaultValue) of
- true ->
- rebar_utils:expand_env_variable(DefaultValue,
- Key,
- VarValue);
- false -> VarValue
- end
- end,
- dict:from_list(Vars),
- dict:from_list(Defaults))).
-
-%%
-%% Given a list of {Key, Value} environment variables, where Key may be defined
-%% multiple times, walk the list and expand each self-reference so that we
-%% end with a list of each variable singly-defined.
-%%
-merge_each_var([], Vars) ->
- Vars;
-merge_each_var([{Key, Value} | Rest], Vars) ->
- Evalue = case orddict:find(Key, Vars) of
- error ->
- %% Nothing yet defined for this key/value.
- %% Expand any self-references as blank.
- rebar_utils:expand_env_variable(Value, Key, "");
- {ok, Value0} ->
- %% Use previous definition in expansion
- rebar_utils:expand_env_variable(Value, Key, Value0)
- end,
- merge_each_var(Rest, orddict:store(Key, Evalue, Vars)).
-
-%%
-%% Give a unique list of {Key, Value} environment variables, expand each one
-%% for every other key until no further expansions are possible.
-%%
-expand_vars_loop(Vars) ->
- expand_vars_loop(Vars, [], dict:from_list(Vars), 10).
-
-expand_vars_loop(_Pending, _Recurse, _Vars, 0) ->
- ?ABORT("Max. expansion reached for ENV vars!\n", []);
-expand_vars_loop([], [], Vars, _Count) ->
- lists:keysort(1, dict:to_list(Vars));
-expand_vars_loop([], Recurse, Vars, Count) ->
- expand_vars_loop(Recurse, [], Vars, Count-1);
-expand_vars_loop([{K, V} | Rest], Recurse, Vars, Count) ->
- %% Identify the variables that need expansion in this value
- ReOpts = [global, {capture, all_but_first, list}, unicode],
- case re:run(V, "\\\${?(\\w+)}?", ReOpts) of
- {match, Matches} ->
- %% Identify the unique variables that need to be expanded
- UniqueMatches = lists:usort([M || [M] <- Matches]),
-
- %% For each variable, expand it and return the final
- %% value. Note that if we have a bunch of unresolvable
- %% variables, nothing happens and we don't bother
- %% attempting further expansion
- case expand_keys_in_value(UniqueMatches, V, Vars) of
- V ->
- %% No change after expansion; move along
- expand_vars_loop(Rest, Recurse, Vars, Count);
- Expanded ->
- %% Some expansion occurred; move to next k/v but
- %% revisit this value in the next loop to check
- %% for further expansion
- NewVars = dict:store(K, Expanded, Vars),
- expand_vars_loop(Rest, [{K, Expanded} | Recurse],
- NewVars, Count)
- end;
-
- nomatch ->
- %% No values in this variable need expansion; move along
- expand_vars_loop(Rest, Recurse, Vars, Count)
- end.
-
-expand_keys_in_value([], Value, _Vars) ->
- Value;
-expand_keys_in_value([Key | Rest], Value, Vars) ->
- NewValue = case dict:find(Key, Vars) of
- {ok, KValue} ->
- rebar_utils:expand_env_variable(Value, Key, KValue);
- error ->
- Value
- end,
- expand_keys_in_value(Rest, NewValue, Vars).
-
-expand_command(TmplName, Env, InFiles, OutFile) ->
- Cmd0 = proplists:get_value(TmplName, Env),
- Cmd1 = rebar_utils:expand_env_variable(Cmd0, "PORT_IN_FILES", InFiles),
- rebar_utils:expand_env_variable(Cmd1, "PORT_OUT_FILE", OutFile).
-
-%%
-%% Given a string, determine if it is expandable
-%%
-is_expandable(InStr) ->
- case re:run(InStr,"\\\$",[{capture,none}]) of
- match -> true;
- nomatch -> false
- end.
-
-%%
-%% Filter a list of env vars such that only those which match the provided
-%% architecture regex (or do not have a regex) are returned.
-%%
-filter_env([], Acc) ->
- lists:reverse(Acc);
-filter_env([{ArchRegex, Key, Value} | Rest], Acc) ->
- case rebar_utils:is_arch(ArchRegex) of
- true ->
- filter_env(Rest, [{Key, Value} | Acc]);
- false ->
- filter_env(Rest, Acc)
- end;
-filter_env([{Key, Value} | Rest], Acc) ->
- filter_env(Rest, [{Key, Value} | Acc]).
-
-erts_dir() ->
- lists:concat([code:root_dir(), "/erts-", erlang:system_info(version)]).
-
-os_env() ->
- ReOpts = [{return, list}, {parts, 2}, unicode],
- Os = [list_to_tuple(re:split(S, "=", ReOpts)) ||
- S <- lists:filter(fun discard_deps_vars/1, os:getenv())],
- %% Drop variables without a name (win32)
- [T1 || {K, _V} = T1 <- Os, K =/= []].
-
-%%
-%% To avoid having multiple repetitions of the same environment variables
-%% (ERL_LIBS), avoid exporting any variables that may cause conflict with
-%% those exported by the rebar_deps module (ERL_LIBS, REBAR_DEPS_DIR)
-%%
-discard_deps_vars("ERL_LIBS=" ++ _Value) -> false;
-discard_deps_vars("REBAR_DEPS_DIR=" ++ _Value) -> false;
-discard_deps_vars(_Var) -> true.
-
-select_compile_template(drv, Compiler) ->
- select_compile_drv_template(Compiler);
-select_compile_template(exe, Compiler) ->
- select_compile_exe_template(Compiler).
-
-select_compile_drv_template("$CC") -> "DRV_CC_TEMPLATE";
-select_compile_drv_template("$CXX") -> "DRV_CXX_TEMPLATE".
-
-select_compile_exe_template("$CC") -> "EXE_CC_TEMPLATE";
-select_compile_exe_template("$CXX") -> "EXE_CXX_TEMPLATE".
-
-select_link_template(Target) ->
- case target_type(Target) of
- drv -> "DRV_LINK_TEMPLATE";
- exe -> "EXE_LINK_TEMPLATE"
- end.
-
-target_type(Target) -> target_type1(filename:extension(Target)).
-
-target_type1(".so") -> drv;
-target_type1(".dll") -> drv;
-target_type1("") -> exe;
-target_type1(".exe") -> exe.
-
-erl_interface_dir(Subdir) ->
- case code:lib_dir(erl_interface, Subdir) of
- {error, bad_name} ->
- throw({error, {erl_interface,Subdir,"code:lib_dir(erl_interface)"
- "is unable to find the erl_interface library."}});
- Dir -> Dir
- end.
-
-default_env() ->
- [
- {"CC" , "cc"},
- {"CXX", "c++"},
- {"DRV_CXX_TEMPLATE",
- "$CXX -c $CXXFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"},
- {"DRV_CC_TEMPLATE",
- "$CC -c $CFLAGS $DRV_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"},
- {"DRV_LINK_TEMPLATE",
- "$CC $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS -o $PORT_OUT_FILE"},
- {"EXE_CXX_TEMPLATE",
- "$CXX -c $CXXFLAGS $EXE_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"},
- {"EXE_CC_TEMPLATE",
- "$CC -c $CFLAGS $EXE_CFLAGS $PORT_IN_FILES -o $PORT_OUT_FILE"},
- {"EXE_LINK_TEMPLATE",
- "$CC $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS -o $PORT_OUT_FILE"},
- {"DRV_CFLAGS" , "-g -Wall -fPIC $ERL_CFLAGS"},
- {"DRV_LDFLAGS", "-shared $ERL_LDFLAGS"},
- {"EXE_CFLAGS" , "-g -Wall -fPIC $ERL_CFLAGS"},
- {"EXE_LDFLAGS", "$ERL_LDFLAGS"},
-
- {"ERL_CFLAGS", lists:concat([" -I\"", erl_interface_dir(include),
- "\" -I\"", filename:join(erts_dir(), "include"),
- "\" "])},
- {"ERL_EI_LIBDIR", lists:concat(["\"", erl_interface_dir(lib), "\""])},
- {"ERL_LDFLAGS" , " -L$ERL_EI_LIBDIR -lerl_interface -lei"},
- {"ERLANG_ARCH" , rebar_utils:wordsize()},
- {"ERLANG_TARGET", rebar_utils:get_arch()},
-
- {"darwin", "DRV_LDFLAGS",
- "-bundle -flat_namespace -undefined suppress $ERL_LDFLAGS"},
-
- %% Solaris specific flags
- {"solaris.*-64$", "CFLAGS", "-D_REENTRANT -m64 $CFLAGS"},
- {"solaris.*-64$", "CXXFLAGS", "-D_REENTRANT -m64 $CXXFLAGS"},
- {"solaris.*-64$", "LDFLAGS", "-m64 $LDFLAGS"},
-
- %% OS X Leopard flags for 64-bit
- {"darwin9.*-64$", "CFLAGS", "-m64 $CFLAGS"},
- {"darwin9.*-64$", "CXXFLAGS", "-m64 $CXXFLAGS"},
- {"darwin9.*-64$", "LDFLAGS", "-arch x86_64 $LDFLAGS"},
-
- %% OS X Snow Leopard, Lion, and Mountain Lion flags for 32-bit
- {"darwin1[0-2].*-32", "CFLAGS", "-m32 $CFLAGS"},
- {"darwin1[0-2].*-32", "CXXFLAGS", "-m32 $CXXFLAGS"},
- {"darwin1[0-2].*-32", "LDFLAGS", "-arch i386 $LDFLAGS"},
-
- %% Windows specific flags
- %% add MS Visual C++ support to rebar on Windows
- {"win32", "CC", "cl.exe"},
- {"win32", "CXX", "cl.exe"},
- {"win32", "LINKER", "link.exe"},
- {"win32", "DRV_CXX_TEMPLATE",
- %% DRV_* and EXE_* Templates are identical
- "$CXX /c $CXXFLAGS $DRV_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"},
- {"win32", "DRV_CC_TEMPLATE",
- "$CC /c $CFLAGS $DRV_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"},
- {"win32", "DRV_LINK_TEMPLATE",
- "$LINKER $PORT_IN_FILES $LDFLAGS $DRV_LDFLAGS /OUT:$PORT_OUT_FILE"},
- %% DRV_* and EXE_* Templates are identical
- {"win32", "EXE_CXX_TEMPLATE",
- "$CXX /c $CXXFLAGS $EXE_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"},
- {"win32", "EXE_CC_TEMPLATE",
- "$CC /c $CFLAGS $EXE_CFLAGS $PORT_IN_FILES /Fo$PORT_OUT_FILE"},
- {"win32", "EXE_LINK_TEMPLATE",
- "$LINKER $PORT_IN_FILES $LDFLAGS $EXE_LDFLAGS /OUT:$PORT_OUT_FILE"},
- %% ERL_CFLAGS are ok as -I even though strictly it should be /I
- {"win32", "ERL_LDFLAGS", " /LIBPATH:$ERL_EI_LIBDIR erl_interface.lib ei.lib"},
- {"win32", "DRV_CFLAGS", "/Zi /Wall $ERL_CFLAGS"},
- {"win32", "DRV_LDFLAGS", "/DLL $ERL_LDFLAGS"}
- ].
diff --git a/src/rebar_protobuffs_compiler.erl b/src/rebar_protobuffs_compiler.erl
deleted file mode 100644
index e89c700..0000000
--- a/src/rebar_protobuffs_compiler.erl
+++ /dev/null
@@ -1,153 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
-%%
-%% 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_protobuffs_compiler).
-
--export([compile/2,
- clean/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
-compile(Config, _AppFile) ->
- case rebar_utils:find_files("src", "^[^._].*\\.proto$") of
- [] ->
- ok;
- FoundFiles ->
- %% Check for protobuffs library -- if it's not present, fail
- %% since we have.proto files that need building
- case protobuffs_is_present() of
- true ->
- %% Build a list of output files - { Proto, Beam, Hrl }
- Targets = [{Proto, beam_file(Proto), hrl_file(Proto)} ||
- Proto <- FoundFiles],
-
- %% Compile each proto file
- compile_each(Config, Targets);
- false ->
- ?ERROR("Protobuffs library not present in code path!\n",
- []),
- ?FAIL
- end
- end.
-
-clean(_Config, _AppFile) ->
- %% Get a list of generated .beam and .hrl files and then delete them
- Protos = rebar_utils:find_files("src", ".*\\.proto$"),
- BeamFiles = [fq_beam_file(F) || F <- Protos],
- HrlFiles = [fq_hrl_file(F) || F <- Protos],
- Targets = BeamFiles ++ HrlFiles,
- case Targets of
- [] ->
- ok;
- _ ->
- delete_each(Targets)
- end.
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, compile) ->
- info_help("Build Protobuffs (*.proto) sources");
-info(help, clean) ->
- info_help("Delete Protobuffs (*.proto) build results").
-
-info_help(Description) ->
- ?CONSOLE(
- "~s.~n"
- "~n"
- "Valid rebar.config options:~n"
- " erl_opts is passed as compile_flags to "
- "protobuffs_compile:scan_file/2~n",
- [Description]).
-
-protobuffs_is_present() ->
- code:which(protobuffs_compile) =/= non_existing.
-
-beam_file(Proto) ->
- filename:basename(Proto, ".proto") ++ "_pb.beam".
-
-hrl_file(Proto) ->
- filename:basename(Proto, ".proto") ++ "_pb.hrl".
-
-fq_beam_file(Proto) ->
- filename:join(["ebin", filename:basename(Proto, ".proto") ++ "_pb.beam"]).
-
-fq_hrl_file(Proto) ->
- filename:join(["include", filename:basename(Proto, ".proto") ++ "_pb.hrl"]).
-
-needs_compile(Proto, Beam) ->
- ActualBeam = filename:join(["ebin", filename:basename(Beam)]),
- filelib:last_modified(ActualBeam) < filelib:last_modified(Proto).
-
-compile_each(_, []) ->
- ok;
-compile_each(Config, [{Proto, Beam, Hrl} | Rest]) ->
- case needs_compile(Proto, Beam) of
- true ->
- ?CONSOLE("Compiling ~s\n", [Proto]),
- ErlOpts = rebar_utils:erl_opts(Config),
- case protobuffs_compile:scan_file(Proto,
- [{compile_flags,ErlOpts}]) of
- ok ->
- %% Compilation worked, but we need to move the
- %% beam and .hrl file into the ebin/ and include/
- %% directories respectively
- %% TODO: Protobuffs really needs to be better about this
- ok = filelib:ensure_dir(filename:join("ebin","dummy")),
- ok = rebar_file_utils:mv(Beam, "ebin"),
- ok = filelib:ensure_dir(filename:join("include", Hrl)),
- ok = rebar_file_utils:mv(Hrl, "include"),
- ok;
- Other ->
- ?ERROR("Protobuffs compile of ~s failed: ~p\n",
- [Proto, Other]),
- ?FAIL
- end;
- false ->
- ok
- end,
- compile_each(Config, Rest).
-
-delete_each([]) ->
- ok;
-delete_each([File | Rest]) ->
- case file:delete(File) of
- ok ->
- ok;
- {error, enoent} ->
- ok;
- {error, Reason} ->
- ?ERROR("Failed to delete ~s: ~p\n", [File, Reason])
- end,
- delete_each(Rest).
diff --git a/src/rebar_provider.erl b/src/rebar_provider.erl
new file mode 100644
index 0000000..e8986f2
--- /dev/null
+++ b/src/rebar_provider.erl
@@ -0,0 +1,139 @@
+-module(rebar_provider).
+
+%% API
+-export([new/2,
+ do/2,
+ impl/1,
+ get_provider/2,
+ get_target_providers/2,
+ format/1]).
+
+-export_type([t/0]).
+
+-include("rebar.hrl").
+
+%%%===================================================================
+%%% Types
+%%%===================================================================
+
+-opaque t() :: record(provider).
+
+-type provider_name() :: atom().
+
+-ifdef(have_callback_support).
+
+-callback init(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+-callback do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+
+-else.
+
+%% In the case where R14 or lower is being used to compile the system
+%% we need to export a behaviour info
+-export([behaviour_info/1]).
+-spec behaviour_info(atom()) -> [{atom(), arity()}] | undefined.
+behaviour_info(callbacks) ->
+ [{init, 1},
+ {do, 1}];
+behaviour_info(_) ->
+ undefined.
+
+-endif.
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%% @doc create a new provider object from the specified module. The
+%% module should implement the provider behaviour.
+%%
+%% @param ModuleName The module name.
+%% @param State0 The current state of the system
+-spec new(module(), rebar_config:config()) ->
+ {ok, rebar_config:config()} | relx:error().
+new(ModuleName, State0) when is_atom(ModuleName) ->
+ case code:which(ModuleName) of
+ non_existing ->
+ ?ERROR("Module ~p does not exist.", [ModuleName]);
+ _ ->
+ ModuleName:init(State0)
+ end.
+
+%% @doc Manipulate the state of the system, that new state
+%%
+%% @param Provider the provider object
+%% @param State the current state of the system
+-spec do(Provider::t(), rebar_config:config()) ->
+ {ok, rebar_config:config()} | relx:error().
+do(Provider, State) ->
+ (Provider#provider.provider_impl):do(State).
+
+%%% @doc get the name of the module that implements the provider
+%%% @param Provider the provider object
+-spec impl(Provider::t()) -> module().
+impl(Provider) ->
+ Provider#provider.name.
+
+%% @doc print the provider module name
+%%
+%% @param T - The provider
+%% @return An iolist describing the provider
+-spec format(t()) -> iolist().
+format(#provider{provider_impl=Mod}) ->
+ erlang:atom_to_list(Mod).
+
+get_target_providers(Targets, State) ->
+ Providers = rebar_config:providers(State),
+ TargetProviders = lists:filter(fun(#provider{provides=T}) ->
+ lists:member(T, Targets)
+ end, Providers),
+ process_deps(TargetProviders, Providers).
+
+-spec get_provider(provider_name(), [t()]) -> t().
+get_provider(ProviderName, [Provider = #provider{name = ProviderName} | _]) ->
+ Provider;
+get_provider(ProviderName, [_ | Rest]) ->
+ get_provider(ProviderName, Rest);
+get_provider(_ProviderName, _) ->
+ [].
+
+process_deps([], _Providers) ->
+ [];
+process_deps(TargetProviders, Providers) ->
+ DepChain = lists:flatmap(fun(Provider) ->
+ {DC, _, _} = process_deps(Provider, Providers, []),
+ DC
+ end, TargetProviders),
+ ['NONE' | Rest] =
+ reorder_providers(lists:flatten([{'NONE', P#provider.name} || P <- TargetProviders] ++ DepChain)),
+ Rest.
+
+process_deps(Provider, Providers, Seen) ->
+ case lists:member(Provider, Seen) of
+ true ->
+ {[], Providers, Seen};
+ false ->
+ Deps = Provider#provider.deps,
+ DepList = lists:map(fun(Dep) ->
+ {Dep, Provider#provider.name}
+ end, Deps),
+ {NewDeps, _, NewSeen} =
+ lists:foldl(fun(Arg, Acc) ->
+ process_dep(Arg, Acc)
+ end,
+ {[], Providers, Seen}, Deps),
+ {[DepList | NewDeps], Providers, NewSeen}
+ end.
+
+process_dep(ProviderName, {Deps, Providers, Seen}) ->
+ Provider = get_provider(ProviderName, Providers),
+ {NewDeps, _, NewSeen} = process_deps(Provider, Providers, [ProviderName | Seen]),
+ {[Deps | NewDeps], Providers, NewSeen}.
+
+%% @doc Reorder the providers according to thier dependency set.
+reorder_providers(OProviderList) ->
+ case rebar_topo:sort(OProviderList) of
+ {ok, ProviderList} ->
+ ProviderList;
+ {cycle, _} ->
+ ?ERROR("There was a cycle in the provider list. Unable to complete build!", [])
+ end.
diff --git a/src/rebar_prv_app_builder.erl b/src/rebar_prv_app_builder.erl
new file mode 100644
index 0000000..6d1498d
--- /dev/null
+++ b/src/rebar_prv_app_builder.erl
@@ -0,0 +1,95 @@
+-module(rebar_prv_app_builder).
+
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, app_builder).
+-define(DEPS, [deps]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = build,
+ bare = false,
+ deps = ?DEPS,
+ example = "rebar build",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ Deps = rebar_config:deps_to_build(Config),
+ Apps = rebar_config:apps_to_build(Config),
+ Config1 =
+ lists:foldl(fun(AppInfo, ConfigAcc) ->
+ ?CONSOLE("Building ~p version ~p~n", [rebar_app_info:name(AppInfo)
+ ,rebar_app_info:original_vsn(AppInfo)]),
+ {_AppInfo1, ConfigAcc1} = build(ConfigAcc, AppInfo),
+ ConfigAcc
+ end, Config, Deps++Apps),
+ Graph = construct_graph(Config),
+ Goals = rebar_config:goals(Config1),
+ {ok, Solve} = rlx_depsolver:solve(Graph, Goals),
+
+ DepsDir = get_deps_dir(Config1),
+ LockDeps = lists:map(fun({Name, _, Source}) ->
+ Dir = get_deps_dir(DepsDir, Name),
+ {Name, Vsn} = lists:keyfind(Name, 1, Solve),
+ rebar_fetch:new(Dir, Name, format_vsn(Vsn), Source)
+ end, rebar_config:deps(Config)),
+ ok = file:write_file("./rebar.lock", io_lib:format("~p.~n", [LockDeps])),
+ {ok, Config1}.
+
+build(Config, AppInfo) ->
+ {ok, AppInfo1} = rebar_otp_app:compile(Config, AppInfo),
+ Config1 = rebar_config:replace_app(Config, rebar_app_info:name(AppInfo1), AppInfo1),
+ rebar_erlc_compiler:compile(Config, rebar_app_info:dir(AppInfo1)),
+ {AppInfo1, Config1}.
+
+%% ===================================================================
+%% Internal functions
+%% ===================================================================
+
+construct_graph(Config) ->
+ LibDirs = rebar_config:get_local(Config, lib_dirs, ["apps", "libs", "."]),
+ DepsDir = rebar_deps:get_deps_dir(Config),
+ Graph = rlx_depsolver:new_graph(),
+ Apps = rebar_app_discover:find_apps([DepsDir | LibDirs]),
+ lists:foldl(fun(AppInfo, GraphAcc) ->
+ Name = rebar_app_info:name(AppInfo),
+ Vsn = rebar_app_info:original_vsn(AppInfo),
+ C = rebar_config:new2(rebar_config:new(), rebar_app_info:dir(AppInfo)),
+ LocalDeps = rebar_config:get_local(C, deps, []),
+ PkgDeps = lists:map(fun({A, "", _}) ->
+ A;
+ ({A, ".*", _}) ->
+ A;
+ ({A, V, _}) ->
+ {A, V}
+ end, LocalDeps),
+ rlx_depsolver:add_package_version(GraphAcc
+ ,Name
+ ,Vsn
+ ,PkgDeps)
+ end, Graph, Apps).
+
+get_deps_dir(Config) ->
+ BaseDir = rebar_utils:base_dir(Config),
+ get_deps_dir(BaseDir, deps).
+
+get_deps_dir(DepsDir, App) ->
+ filename:join(DepsDir, atom_to_list(App)).
+
+format_vsn(Vsn) ->
+ binary_to_list(iolist_to_binary(ec_semver:format(Vsn))).
diff --git a/src/rebar_prv_release.erl b/src/rebar_prv_release.erl
new file mode 100644
index 0000000..3edf2eb
--- /dev/null
+++ b/src/rebar_prv_release.erl
@@ -0,0 +1,37 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_prv_release).
+
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, release).
+-define(DEPS, [app_builder]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = release,
+ bare = false,
+ deps = ?DEPS,
+ example = "rebar release",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ RelxConfig = rebar_config:get_local(Config, relx, []),
+ relx:main("release"),
+ {ok, Config}.
diff --git a/src/rebar_prv_tar.erl b/src/rebar_prv_tar.erl
new file mode 100644
index 0000000..d45c70d
--- /dev/null
+++ b/src/rebar_prv_tar.erl
@@ -0,0 +1,37 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_prv_tar).
+
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, tar).
+-define(DEPS, []).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = tar,
+ bare = false,
+ deps = ?DEPS,
+ example = "rebar tar",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ RelxConfig = rebar_config:get_local(Config, relx, []),
+ relx:main("release tar"),
+ {ok, Config}.
diff --git a/src/rebar_rel_utils.erl b/src/rebar_rel_utils.erl
deleted file mode 100644
index 5d99948..0000000
--- a/src/rebar_rel_utils.erl
+++ /dev/null
@@ -1,246 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
-%%
-%% 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_rel_utils).
-
--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_rel_apps/1,
- get_rel_apps/2,
- get_previous_release_path/1,
- get_rel_file_path/2,
- load_config/2,
- get_sys_tuple/1,
- get_excl_lib_tuple/1,
- get_target_dir/2,
- get_root_dir/2,
- get_target_parent_dir/2]).
-
--include("rebar.hrl").
-
-is_rel_dir() ->
- is_rel_dir(rebar_utils:get_cwd()).
-
-is_rel_dir(Dir) ->
- Fname = filename:join([Dir, "reltool.config"]),
- Scriptname = Fname ++ ".script",
- Res = case filelib:is_regular(Scriptname) of
- true ->
- {true, Scriptname};
- false ->
- case filelib:is_regular(Fname) of
- true ->
- {true, Fname};
- false ->
- false
- end
- end,
- ?DEBUG("is_rel_dir(~s) -> ~p~n", [Dir, Res]),
- Res.
-
-%% Get release name and version from a reltool.config
-get_reltool_release_info([{sys, Config}| _]) ->
- {rel, Name, Ver, _} = proplists:lookup(rel, Config),
- {Name, Ver};
-get_reltool_release_info(ReltoolFile) when is_list(ReltoolFile) ->
- case file:consult(ReltoolFile) of
- {ok, ReltoolConfig} ->
- get_reltool_release_info(ReltoolConfig);
- _ ->
- ?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) ->
- RelPath = get_rel_file_path(Name, Path),
- get_rel_release_info(RelPath).
-
-%% Get list of apps included in a release from a rel file
-get_rel_apps(RelFile) ->
- case file:consult(RelFile) of
- {ok, [{release, _, _, Apps}]} ->
- make_proplist(Apps, []);
- _ ->
- ?ABORT("Failed to parse ~s~n", [RelFile])
- end.
-
-%% Get list of apps included in a release from a name and a path
-get_rel_apps(Name, Path) ->
- RelPath = get_rel_file_path(Name, Path),
- get_rel_apps(RelPath).
-
-%% Get rel file path from name and path
-get_rel_file_path(Name, Path) ->
- [RelFile] = filelib:wildcard(filename:join([Path, "releases", "*",
- Name ++ ".rel"])),
- RelFile.
-
-%% Get the previous release path from a global variable
-get_previous_release_path(Config) ->
- case rebar_config:get_global(Config, previous_release, false) of
- false ->
- ?ABORT("previous_release=PATH is required to "
- "create upgrade package~n", []);
- OldVerPath ->
- OldVerPath
- end.
-
-%%
-%% Load terms from reltool.config
-%%
-load_config(Config, ReltoolFile) ->
- case rebar_config:consult_file(ReltoolFile) of
- {ok, Terms} ->
- expand_version(Config, Terms, filename:dirname(ReltoolFile));
- Other ->
- ?ABORT("Failed to load expected config from ~s: ~p\n",
- [ReltoolFile, Other])
- end.
-
-%%
-%% Look for the {sys, [...]} tuple in the reltool.config file.
-%% Without this present, we can't run reltool.
-%%
-get_sys_tuple(ReltoolConfig) ->
- case lists:keyfind(sys, 1, ReltoolConfig) of
- {sys, _} = SysTuple ->
- SysTuple;
- false ->
- ?ABORT("Failed to find {sys, [...]} tuple in reltool.config.", [])
- end.
-
-%%
-%% Look for the {excl_lib, ...} tuple in sys tuple of the reltool.config file.
-%% Without this present, return false.
-%%
-get_excl_lib_tuple(ReltoolConfig) ->
- lists:keyfind(excl_lib, 1, element(2, get_sys_tuple(ReltoolConfig))).
-
-%%
-%% Look for {target_dir, TargetDir} in the reltool config file; if none is
-%% found, use the name of the release as the default target directory.
-%%
-get_target_dir(Config, ReltoolConfig) ->
- case rebar_config:get_global(Config, target_dir, undefined) of
- undefined ->
- case lists:keyfind(target_dir, 1, ReltoolConfig) of
- {target_dir, TargetDir} ->
- filename:absname(TargetDir);
- false ->
- {sys, SysInfo} = get_sys_tuple(ReltoolConfig),
- case lists:keyfind(rel, 1, SysInfo) of
- {rel, Name, _Vsn, _Apps} ->
- filename:absname(Name);
- false ->
- filename:absname("target")
- end
- end;
- TargetDir ->
- filename:absname(TargetDir)
- end.
-
-get_target_parent_dir(Config, ReltoolConfig) ->
- TargetDir = get_target_dir(Config, ReltoolConfig),
- case lists:reverse(tl(lists:reverse(filename:split(TargetDir)))) of
- [] -> ".";
- Components -> filename:join(Components)
- end.
-
-%%
-%% Look for root_dir in sys tuple and command line; fall back to
-%% code:root_dir().
-%%
-get_root_dir(Config, ReltoolConfig) ->
- {sys, SysInfo} = get_sys_tuple(ReltoolConfig),
- SysRootDirTuple = lists:keyfind(root_dir, 1, SysInfo),
- CmdRootDir = rebar_config:get_global(Config, root_dir, undefined),
- case {SysRootDirTuple, CmdRootDir} of
- %% root_dir in sys typle and no root_dir on cmd-line
- {{root_dir, SysRootDir}, undefined} ->
- SysRootDir;
- %% root_dir in sys typle and also root_dir on cmd-line
- {{root_dir, SysRootDir}, CmdRootDir} when CmdRootDir =/= undefined ->
- case string:equal(SysRootDir, CmdRootDir) of
- true ->
- ok;
- false ->
- ?WARN("overriding reltool.config root_dir with "
- "different command line root_dir~n", [])
- end,
- CmdRootDir;
- %% no root_dir in sys typle and no root_dir on cmd-line
- {false, undefined} ->
- code:root_dir();
- %% no root_dir in sys tuple but root_dir on cmd-line
- {false, CmdRootDir} when CmdRootDir =/= undefined ->
- CmdRootDir
- end.
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-make_proplist([{_,_}=H|T], Acc) ->
- make_proplist(T, [H|Acc]);
-make_proplist([H|T], Acc) ->
- App = element(1, H),
- Ver = element(2, H),
- make_proplist(T, [{App,Ver}|Acc]);
-make_proplist([], Acc) ->
- Acc.
-
-expand_version(Config, ReltoolConfig, Dir) ->
- case lists:keyfind(sys, 1, ReltoolConfig) of
- {sys, Sys} ->
- {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)};
- _ ->
- {Config, ReltoolConfig}
- end.
-
-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}.
diff --git a/src/rebar_reltool.erl b/src/rebar_reltool.erl
deleted file mode 100644
index fdaa7e0..0000000
--- a/src/rebar_reltool.erl
+++ /dev/null
@@ -1,425 +0,0 @@
-%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
-%% ex: ts=4 sw=4 et
-%% -------------------------------------------------------------------
-%%
-%% rebar: Erlang Build Tools
-%%
-%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
-%%
-%% 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_reltool).
-
--export([generate/2,
- overlay/2,
- clean/2]).
-
-%% for internal use only
--export([info/2]).
-
--include("rebar.hrl").
--include_lib("kernel/include/file.hrl").
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-
-generate(Config0, ReltoolFile) ->
- %% Make sure we have decent version of reltool available
- check_vsn(),
-
- %% Load the reltool configuration from the file
- {Config, ReltoolConfig} = rebar_rel_utils:load_config(Config0, ReltoolFile),
-
- Sys = rebar_rel_utils:get_sys_tuple(ReltoolConfig),
-
- %% Spin up reltool server and load our config into it
- {ok, Server} = reltool:start_server([Sys]),
-
- %% Do some validation of the reltool configuration; error messages out of
- %% reltool are still pretty cryptic
- validate_rel_apps(Server, Sys),
-
- %% Finally, run reltool
- case catch(run_reltool(Server, Config, ReltoolConfig)) of
- ok ->
- {ok, Config};
- {error, failed} ->
- ?FAIL;
- Other2 ->
- ?ERROR("Unexpected error: ~p\n", [Other2]),
- ?FAIL
- end.
-
-overlay(Config, ReltoolFile) ->
- %% Load the reltool configuration from the file
- {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
- {process_overlay(Config, ReltoolConfig), Config1}.
-
-clean(Config, ReltoolFile) ->
- {Config1, ReltoolConfig} = rebar_rel_utils:load_config(Config, ReltoolFile),
- TargetDir = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
- rebar_file_utils:rm_rf(TargetDir),
- rebar_file_utils:delete_each(["reltool.spec"]),
- {ok, Config1}.
-
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-info(help, generate) ->
- info_help("Build release with reltool");
-info(help, clean) ->
- info_help("Delete release");
-info(help, overlay) ->
- info_help("Run reltool overlays only").
-
-info_help(Description) ->
- ?CONSOLE(
- "~s.~n"
- "~n"
- "Valid rebar.config options:~n"
- " ~n"
- "Valid reltool.config options:~n"
- " {sys, []}~n"
- " {target_dir, \"target\"}~n"
- " {overlay_vars, \"overlay\"}~n"
- " {overlay, []}~n"
- "Valid command line options:~n"
- " target_dir=target~n"
- " overlay_vars=VarsFile~n"
- " dump_spec=1 (write reltool target spec to reltool.spec)~n",
- [
- Description
- ]).
-
-check_vsn() ->
- %% TODO: use application:load and application:get_key once we require
- %% R14A or newer. There's no reltool.app before R14A.
- case code:lib_dir(reltool) of
- {error, bad_name} ->
- ?ABORT("Reltool support requires the reltool application "
- "to be installed!", []);
- Path ->
- ReltoolVsn = filename:basename(Path),
- case ReltoolVsn < "reltool-0.5.2" of
- true ->
- ?ABORT("Reltool support requires at least reltool-0.5.2; "
- "this VM is using ~s\n", [ReltoolVsn]);
- false ->
- ok
- end
- end.
-
-process_overlay(Config, ReltoolConfig) ->
- TargetDir = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
-
- {_BootRelName, BootRelVsn} =
- rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
-
- %% Initialize overlay vars with some basics
- %% (that can get overwritten)
- OverlayVars0 =
- dict:from_list([{erts_vsn, "erts-" ++ erlang:system_info(version)},
- {rel_vsn, BootRelVsn},
- {target_dir, TargetDir},
- {hostname, net_adm:localhost()}]),
-
- %% Load up any variables specified by overlay_vars
- OverlayVars1 = overlay_vars(Config, OverlayVars0, ReltoolConfig),
- OverlayVars = rebar_templater:resolve_variables(dict:to_list(OverlayVars1),
- OverlayVars1),
-
- %% Finally, overlay the files specified by the overlay section
- case overlay_files(ReltoolConfig) of
- [] ->
- ok;
- Overlay ->
- execute_overlay(Overlay, OverlayVars, rebar_utils:get_cwd(),
- TargetDir)
- end.
-
-%%
-%% Look for overlay_vars file reference. If the user provides an overlay_vars on
-%% the command line (i.e. a global), the terms from that file OVERRIDE the one
-%% listed in reltool.config. To re-iterate, this means you can specify a
-%% variable in the file from reltool.config and then override that value by
-%% providing an additional file on the command-line.
-%%
-overlay_vars(Config, Vars0, ReltoolConfig) ->
- BaseVars = load_vars_file([proplists:get_value(overlay_vars, ReltoolConfig)]),
- OverlayVars = rebar_config:get_global(Config, overlay_vars, []),
- OverrideVars = load_vars_file(string:tokens(OverlayVars, ",")),
- M = fun merge_overlay_var/3,
- dict:merge(M, dict:merge(M, Vars0, BaseVars), OverrideVars).
-
-merge_overlay_var(_Key, _Base, Override) -> Override.
-
-%%
-%% If a filename is provided, construct a dict of terms
-%%
-load_vars_file([undefined]) ->
- dict:new();
-load_vars_file([]) ->
- dict:new();
-load_vars_file(Files) ->
- load_vars_file(Files, dict:new()).
-
-load_vars_file([], Dict) ->
- Dict;
-load_vars_file([File | Files], BaseVars) ->
- case rebar_config:consult_file(File) of
- {ok, Terms} ->
- OverrideVars = dict:from_list(Terms),
- M = fun merge_overlay_var/3,
- load_vars_file(Files, dict:merge(M, BaseVars, OverrideVars));
- {error, Reason} ->
- ?ABORT("Unable to load overlay_vars from ~p: ~p\n", [File, Reason])
- end.
-
-validate_rel_apps(ReltoolServer, {sys, ReltoolConfig}) ->
- case lists:keyfind(rel, 1, ReltoolConfig) of
- false ->
- ok;
- {rel, _Name, _Vsn, Apps} ->
- %% Identify all the apps that do NOT exist, based on
- %% what's available from the reltool server
- Missing = lists:sort(
- [App || App <- Apps,
- app_exists(App, ReltoolServer) == false]),
- case Missing of
- [] ->
- ok;
- _ ->
- ?ABORT("Apps in {rel, ...} section not found by "
- "reltool: ~p\n", [Missing])
- end;
- Rel ->
- %% Invalid release format!
- ?ABORT("Invalid {rel, ...} section in reltools.config: ~p\n", [Rel])
- end.
-
-app_exists(App, Server) when is_atom(App) ->
- case reltool_server:get_app(Server, App) of
- {ok, _} ->
- true;
- _ ->
- false
- end;
-app_exists(AppTuple, Server) when is_tuple(AppTuple) ->
- app_exists(element(1, AppTuple), Server).
-
-run_reltool(Server, Config, ReltoolConfig) ->
- case reltool:get_target_spec(Server) of
- {ok, Spec} ->
- %% Pull the target dir and make sure it exists
- TargetDir = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
- mk_target_dir(Config, TargetDir),
-
- %% Determine the otp root dir to use
- RootDir = rebar_rel_utils:get_root_dir(Config, ReltoolConfig),
-
- %% Dump the spec, if necessary
- dump_spec(Config, Spec),
-
- %% Have reltool actually run
- case reltool:eval_target_spec(Spec, RootDir, TargetDir) of
- ok ->
- ok;
- {error, Reason} ->
- ?ABORT("Failed to generate target from spec: ~p\n",
- [Reason])
- end,
-
- {BootRelName, BootRelVsn} =
- rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
-
- ok = create_RELEASES(TargetDir, BootRelName, BootRelVsn),
-
- process_overlay(Config, ReltoolConfig);
-
- {error, Reason} ->
- ?ABORT("Unable to generate spec: ~s\n", [Reason])
- end.
-
-mk_target_dir(Config, TargetDir) ->
- case filelib:ensure_dir(filename:join(TargetDir, "dummy")) of
- ok ->
- ok;
- {error, eexist} ->
- %% Output directory already exists; if force=1, wipe it out
- case rebar_config:get_global(Config, force, "0") of
- "1" ->
- rebar_file_utils:rm_rf(TargetDir),
- ok = file:make_dir(TargetDir);
- _ ->
- ?ERROR("Release target directory ~p already exists!\n",
- [TargetDir]),
- ?FAIL
- end;
- {error, Reason} ->
- ?ERROR("Failed to make target dir ~p: ~s\n",
- [TargetDir, file:format_error(Reason)]),
- ?FAIL
- end.
-
-dump_spec(Config, Spec) ->
- case rebar_config:get_global(Config, dump_spec, "0") of
- "1" ->
- SpecBin = list_to_binary(io_lib:print(Spec, 1, 120, -1)),
- ok = file:write_file("reltool.spec", SpecBin);
- _ ->
- ok
- end.
-
-
-overlay_files(ReltoolConfig) ->
- Original = case lists:keyfind(overlay, 1, ReltoolConfig) of
- {overlay, Overlay} when is_list(Overlay) ->
- Overlay;
- false ->
- ?INFO("No {overlay, [...]} found in reltool.config.\n", []),
- [];
- _ ->
- ?ABORT("{overlay, [...]} entry in reltool.config "
- "must be a list.\n", [])
- end,
- SlimAddition = case rebar_rel_utils:get_excl_lib_tuple(ReltoolConfig) of
- {excl_lib, otp_root} ->
- [{create, "releases/{{rel_vsn}}/runner_script.data",
- "slim\n"}];
- false ->
- []
- end,
- Original ++ SlimAddition.
-
-%% TODO: Merge functionality here with rebar_templater
-
-execute_overlay([], _Vars, _BaseDir, _TargetDir) ->
- ok;
-execute_overlay([{mkdir, Out} | Rest], Vars, BaseDir, TargetDir) ->
- OutFile = rebar_templater:render(
- filename:join([TargetDir, Out, "dummy"]), Vars),
- ok = filelib:ensure_dir(OutFile),
- ?DEBUG("Created dir ~s\n", [filename:dirname(OutFile)]),
- execute_overlay(Rest, Vars, BaseDir, TargetDir);
-execute_overlay([{copy, In} | Rest], _Vars, BaseDir, TargetDir) ->
- execute_overlay([{copy, In, ""} | Rest], _Vars, BaseDir, TargetDir);
-execute_overlay([{copy, In, Out} | Rest], Vars, BaseDir, TargetDir) ->
- InFile = rebar_templater:render(filename:join(BaseDir, In), Vars),
- OutFile = rebar_templater:render(filename:join(TargetDir, Out), Vars),
- case filelib:is_dir(InFile) of
- true ->
- ok;
- false ->
- ok = filelib:ensure_dir(OutFile)
- end,
- rebar_file_utils:cp_r([InFile], OutFile),
- execute_overlay(Rest, Vars, BaseDir, TargetDir);
-execute_overlay([{template_wildcard, Wildcard, OutDir} | Rest], Vars,
- BaseDir, TargetDir) ->
- %% Generate a series of {template, In, Out} instructions from the wildcard
- %% that will get processed per normal
- Ifun = fun(F, Acc0) ->
- [{template, F,
- filename:join(OutDir, filename:basename(F))} | Acc0]
- end,
- NewInstrs = lists:foldl(Ifun, Rest, filelib:wildcard(Wildcard, BaseDir)),
- case length(NewInstrs) =:= length(Rest) of
- true ->
- ?WARN("template_wildcard: ~s did not match any files!\n",
- [Wildcard]);
- false ->
- ok
- end,
- ?DEBUG("template_wildcard: ~s expanded to ~p\n", [Wildcard, NewInstrs]),
- execute_overlay(NewInstrs, Vars, BaseDir, TargetDir);
-execute_overlay([{template, In, Out} | Rest], Vars, BaseDir, TargetDir) ->
- InFile = rebar_templater:render(filename:join(BaseDir, In), Vars),
- {ok, InFileData} = file:read_file(InFile),
- OutFile = rebar_templater:render(filename:join(TargetDir, Out), Vars),
- ok = filelib:ensure_dir(OutFile),
- case file:write_file(OutFile, rebar_templater:render(InFileData, Vars)) of
- ok ->
- ok = apply_file_info(InFile, OutFile),
- ?DEBUG("Templated ~p\n", [OutFile]),
- execute_overlay(Rest, Vars, BaseDir, TargetDir);
- {error, Reason} ->
- ?ABORT("Failed to template ~p: ~p\n", [OutFile, Reason])
- end;
-execute_overlay([{create, Out, Contents} | Rest], Vars, BaseDir, TargetDir) ->
- OutFile = rebar_templater:render(filename:join(TargetDir, Out), Vars),
- ok = filelib:ensure_dir(OutFile),
- case file:write_file(OutFile, Contents) of
- ok ->
- ?DEBUG("Created ~p\n", [OutFile]),
- execute_overlay(Rest, Vars, BaseDir, TargetDir);
- {error, Reason} ->
- ?ABORT("Failed to create ~p: ~p\n", [OutFile, Reason])
- end;
-execute_overlay([{replace, Out, Regex, Replacement} | Rest],
- Vars, BaseDir, TargetDir) ->
- execute_overlay([{replace, Out, Regex, Replacement, []} | Rest],
- Vars, BaseDir, TargetDir);
-execute_overlay([{replace, Out, Regex, Replacement, Opts} | Rest],
- Vars, BaseDir, TargetDir) ->
- Filename = rebar_templater:render(filename:join(TargetDir, Out), Vars),
- {ok, OrigData} = file:read_file(Filename),
- Data = re:replace(OrigData, Regex,
- rebar_templater:render(Replacement, Vars),
- [global, {return, binary}] ++ Opts),
- case file:write_file(Filename, Data) of
- ok ->
- ?DEBUG("Edited ~s: s/~s/~s/\n", [Filename, Regex, Replacement]),
- execute_overlay(Rest, Vars, BaseDir, TargetDir);
- {error, Reason} ->
- ?ABORT("Failed to edit ~p: ~p\n", [Filename, Reason])
- end;
-execute_overlay([Other | _Rest], _Vars, _BaseDir, _TargetDir) ->
- {error, {unsupported_operation, Other}}.
-
-
-apply_file_info(InFile, OutFile) ->
- {ok, FileInfo} = file:read_file_info(InFile),
- ok = file:write_file_info(OutFile, FileInfo).
-
-create_RELEASES(TargetDir, RelName, RelVsn) ->
- ReleasesDir = filename:join(TargetDir, "releases"),
- RelFile = filename:join([ReleasesDir, RelVsn, RelName ++ ".rel"]),
- Apps = rebar_rel_utils:get_rel_apps(RelFile),
- TargetLib = filename:join(TargetDir,"lib"),
-
- AppDirs =
- [ {App, Vsn, TargetLib}
- || {App, Vsn} <- Apps,
- filelib:is_dir(
- filename:join(TargetLib,
- lists:concat([App, "-", Vsn]))) ],
-
- case release_handler:create_RELEASES(
- code:root_dir(),
- ReleasesDir,
- RelFile,
- AppDirs) of
- ok ->
- ok;
- {error, Reason} ->
- ?ABORT("Failed to create RELEASES file: ~p\n",
- [Reason])
- end.
diff --git a/src/rebar_shell.erl b/src/rebar_shell.erl
index 0220a79..b089631 100644
--- a/src/rebar_shell.erl
+++ b/src/rebar_shell.erl
@@ -28,9 +28,37 @@
-module(rebar_shell).
-author("Kresten Krab Thorup <krab@trifork.com>").
+-behaviour(rebar_provider).
+
+-export([init/1,
+ do/1]).
+
-include("rebar.hrl").
--export([shell/2, info/2]).
+-define(PROVIDER, shell).
+-define(DEPS, [app_builder]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_config:config()) -> {ok, rebar_config:config()}.
+init(State) ->
+ State1 = rebar_config:add_provider(State, #provider{name = ?PROVIDER,
+ provider_impl = ?MODULE,
+ provides = shell,
+ bare = false,
+ deps = ?DEPS,
+ example = "rebar shell",
+ short_desc = "",
+ desc = "",
+ opts = []}),
+ {ok, State1}.
+
+-spec do(rebar_config:config()) -> {ok, rebar_config:config()} | relx:error().
+do(Config) ->
+ shell(),
+ {ok, Config}.
%% NOTE:
%% this is an attempt to replicate `erl -pa ./ebin -pa deps/*/ebin`. it is
@@ -39,7 +67,7 @@
%% it also lacks the ctrl-c interrupt handler that `erl` features. ctrl-c will
%% immediately kill the script. ctrl-g, however, works fine
-shell(_Config, _AppFile) ->
+shell() ->
true = code:add_pathz(rebar_utils:ebin_dir()),
%% scan all processes for any with references to the old user and save them to
%% update later
@@ -88,4 +116,4 @@ wait_until_user_started(Timeout) ->
%% if user is not yet registered wait a tenth of a second and try again
undefined -> timer:sleep(100), wait_until_user_started(Timeout - 100);
_ -> ok
- end. \ No newline at end of file
+ end.
diff --git a/src/rebar_topo.erl b/src/rebar_topo.erl
new file mode 100644
index 0000000..d6447c5
--- /dev/null
+++ b/src/rebar_topo.erl
@@ -0,0 +1,219 @@
+%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 80 -*-
+%%% Copyright 2012 Erlware, LLC. All Rights Reserved.
+%%%
+%%% This file is provided to you under the Apache License,
+%%% Version 2.0 (the "License"); you may not use this file
+%%% except in compliance with the License. You may obtain
+%%% a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing,
+%%% software distributed under the License is distributed on an
+%%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%%% KIND, either express or implied. See the License for the
+%%% specific language governing permissions and limitations
+%%% under the License.
+%%%-------------------------------------------------------------------
+%%% @author Joe Armstrong
+%%% @author Eric Merritt
+%%% @doc
+%%% This is a pretty simple topological sort for erlang. It was
+%%% originally written for ermake by Joe Armstrong back in '98. It
+%%% has been pretty heavily modified by Eric Merritt since '06 and modified again for Relx.
+%%%
+%%% A partial order on the set S is a set of pairs {Xi,Xj} such that
+%%% some relation between Xi and Xj is obeyed.
+%%%
+%%% A topological sort of a partial order is a sequence of elements
+%%% [X1, X2, X3 ...] such that if whenever {Xi, Xj} is in the partial
+%%% order i &lt; j
+%%% @end
+%%%-------------------------------------------------------------------
+-module(rebar_topo).
+
+-export([sort/1,
+ sort_apps/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+%%====================================================================
+%% Types
+%%====================================================================
+-type pair() :: {DependentApp::atom(), PrimaryApp::atom()}.
+-type name() :: AppName::atom().
+-type element() :: name() | pair().
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%% @doc This only does a topo sort on the list of applications and
+%% assumes that there is only *one* version of each app in the list of
+%% applications. This implies that you have already done the
+%% constraint solve before you pass the list of apps here to be
+%% sorted.
+-spec sort_apps([rlx_app_info:t()]) ->
+ {ok, [rlx_app_info:t()]} |
+ relx:error().
+sort_apps(Apps) ->
+ Pairs = apps_to_pairs(Apps),
+ case sort(Pairs) of
+ {ok, Names} ->
+ {ok, names_to_apps(Names, Apps)};
+ E ->
+ E
+ end.
+
+%% @doc Do a topological sort on the list of pairs.
+-spec sort([pair()]) -> {ok, [atom()]} | relx:error().
+sort(Pairs) ->
+ iterate(Pairs, [], all(Pairs)).
+
+%% @doc nicely format the error from the sort.
+-spec format_error(Reason::term()) -> iolist().
+format_error({cycle, Pairs}) ->
+ ["Cycle detected in dependency graph, this must be resolved "
+ "before we can continue:\n",
+ case Pairs of
+ [{P1, P2}] ->
+ [rlx_util:indent(2), erlang:atom_to_list(P2), "->", erlang:atom_to_list(P1)];
+ [{P1, P2} | Rest] ->
+ [rlx_util:indent(2), erlang:atom_to_list(P2), "->", erlang:atom_to_list(P1),
+ [["-> ", erlang:atom_to_list(PP2), " -> ", erlang:atom_to_list(PP1)] || {PP1, PP2} <- Rest]];
+ [] ->
+ []
+ end].
+
+%%====================================================================
+%% Internal Functions
+%%====================================================================
+-spec names_to_apps([atom()], [rlx_app_info:t()]) -> [rlx_app_info:t()].
+names_to_apps(Names, Apps) ->
+ [find_app_by_name(Name, Apps) || Name <- Names].
+
+-spec find_app_by_name(atom(), [rlx_app_info:t()]) -> rlx_app_info:t().
+find_app_by_name(Name, Apps) ->
+ {ok, App1} =
+ ec_lists:find(fun(App) ->
+ rlx_app_info:name(App) =:= Name
+ end, Apps),
+ App1.
+
+-spec apps_to_pairs([rlx_app_info:t()]) -> [pair()].
+apps_to_pairs(Apps) ->
+ lists:flatten([app_to_pairs(App) || App <- Apps]).
+
+-spec app_to_pairs(rlx_app_info:t()) -> [pair()].
+app_to_pairs(App) ->
+ [{DepApp, rlx_app_info:name(App)} ||
+ DepApp <-
+ rlx_app_info:active_deps(App) ++
+ rlx_app_info:library_deps(App)].
+
+
+%% @doc Iterate over the system. @private
+-spec iterate([pair()], [name()], [name()]) ->
+ {ok, [name()]} | relx:error().
+iterate([], L, All) ->
+ {ok, remove_duplicates(L ++ subtract(All, L))};
+iterate(Pairs, L, All) ->
+ case subtract(lhs(Pairs), rhs(Pairs)) of
+ [] ->
+ ?ERROR(format_error({cycle, Pairs}), []);
+ Lhs ->
+ iterate(remove_pairs(Lhs, Pairs), L ++ Lhs, All)
+ end.
+
+-spec all([pair()]) -> [atom()].
+all(L) ->
+ lhs(L) ++ rhs(L).
+
+-spec lhs([pair()]) -> [atom()].
+lhs(L) ->
+ [X || {X, _} <- L].
+
+-spec rhs([pair()]) -> [atom()].
+rhs(L) ->
+ [Y || {_, Y} <- L].
+
+%% @doc all the elements in L1 which are not in L2
+%% @private
+-spec subtract([element()], [element()]) -> [element()].
+subtract(L1, L2) ->
+ [X || X <- L1, not lists:member(X, L2)].
+
+%% @doc remove dups from the list. @private
+-spec remove_duplicates([element()]) -> [element()].
+remove_duplicates([H|T]) ->
+ case lists:member(H, T) of
+ true ->
+ remove_duplicates(T);
+ false ->
+ [H|remove_duplicates(T)]
+ end;
+remove_duplicates([]) ->
+ [].
+
+%% @doc
+%% removes all pairs from L2 where the first element
+%% of each pair is a member of L1
+%%
+%% L2' L1 = [X] L2 = [{X,Y}].
+%% @private
+-spec remove_pairs([atom()], [pair()]) -> [pair()].
+remove_pairs(L1, L2) ->
+ [All || All={X, _Y} <- L2, not lists:member(X, L1)].
+
+%%====================================================================
+%% Tests
+%%====================================================================
+-ifndef(NOTEST).
+-include_lib("eunit/include/eunit.hrl").
+
+topo_1_test() ->
+ Pairs = [{one,two},{two,four},{four,six},
+ {two,ten},{four,eight},
+ {six,three},{one,three},
+ {three,five},{five,eight},
+ {seven,five},{seven,nine},
+ {nine,four},{nine,ten}],
+ ?assertMatch({ok, [one,seven,two,nine,four,six,three,five,eight,ten]},
+ sort(Pairs)).
+topo_2_test() ->
+ Pairs = [{app2, app1}, {zapp1, app1}, {stdlib, app1},
+ {app3, app2}, {kernel, app1}, {kernel, app3},
+ {app2, zapp1}, {app3, zapp1}, {zapp2, zapp1}],
+ ?assertMatch({ok, [stdlib, kernel, zapp2,
+ app3, app2, zapp1, app1]},
+ sort(Pairs)).
+
+topo_pairs_cycle_test() ->
+ Pairs = [{app2, app1}, {app1, app2}, {stdlib, app1}],
+ ?assertMatch({error, {_, {cycle, [{app2, app1}, {app1, app2}]}}},
+ sort(Pairs)).
+
+topo_apps_cycle_test() ->
+ {ok, App1} = rlx_app_info:new(app1, "0.1", "/no-dir", [app2], [stdlib]),
+ {ok, App2} = rlx_app_info:new(app2, "0.1", "/no-dir", [app1], []),
+ Apps = [App1, App2],
+ ?assertMatch({error, {_, {cycle, [{app2,app1},{app1,app2}]}}},
+ sort_apps(Apps)).
+
+topo_apps_good_test() ->
+ Apps = [App ||
+ {ok, App} <-
+ [rlx_app_info:new(app1, "0.1", "/no-dir", [app2, zapp1], [stdlib, kernel]),
+ rlx_app_info:new(app2, "0.1", "/no-dir", [app3], []),
+ rlx_app_info:new(app3, "0.1", "/no-dir", [kernel], []),
+ rlx_app_info:new(zapp1, "0.1", "/no-dir", [app2,app3,zapp2], []),
+ rlx_app_info:new(stdlib, "0.1", "/no-dir", [], []),
+ rlx_app_info:new(kernel, "0.1", "/no-dir", [], []),
+ rlx_app_info:new(zapp2, "0.1", "/no-dir", [], [])]],
+ {ok, Sorted} = sort_apps(Apps),
+ ?assertMatch([stdlib, kernel, zapp2,
+ app3, app2, zapp1, app1],
+ [rlx_app_info:name(App) || App <- Sorted]).
+
+-endif.
diff --git a/src/rebar_upgrade.erl b/src/rebar_upgrade.erl
deleted file mode 100644
index 3a38a08..0000000
--- a/src/rebar_upgrade.erl
+++ /dev/null
@@ -1,266 +0,0 @@
-%% -*- 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_upgrade).
-
--include("rebar.hrl").
--include_lib("kernel/include/file.hrl").
-
--export(['generate-upgrade'/2]).
-
-%% for internal use only
--export([info/2]).
-
--define(TMP, "_tmp").
-
-%% ====================================================================
-%% Public API
-%% ====================================================================
-
-'generate-upgrade'(Config0, ReltoolFile) ->
- %% Get the old release path
- {Config, ReltoolConfig} = rebar_rel_utils:load_config(Config0, ReltoolFile),
- TargetParentDir = rebar_rel_utils:get_target_parent_dir(Config,
- ReltoolConfig),
- TargetDir = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
-
- PrevRelPath = rebar_rel_utils:get_previous_release_path(Config),
- OldVerPath = filename:join([TargetParentDir, PrevRelPath]),
-
- %% Run checks to make sure that building a package is possible
- {NewVerPath, NewName, NewVer} = run_checks(Config, OldVerPath,
- ReltoolConfig),
- NameVer = NewName ++ "_" ++ NewVer,
-
- %% Save the code path prior to doing anything
- OrigPath = code:get_path(),
-
- %% Prepare the environment for building the package
- ok = setup(OldVerPath, NewVerPath, NewName, NewVer, NameVer),
-
- %% Build the package
- run_systools(NameVer, NewName),
-
- %% Boot file changes
- {ok, _} = boot_files(TargetDir, NewVer, NewName),
-
- %% Extract upgrade and tar it back up with changes
- make_tar(NameVer, NewVer, NewName),
-
- %% Clean up files that systools created
- ok = cleanup(NameVer),
-
- %% Restore original path
- true = code:set_path(OrigPath),
-
- {ok, Config}.
-
-%% ===================================================================
-%% Internal functions
-%% ==================================================================
-
-info(help, 'generate-upgrade') ->
- ?CONSOLE("Build an upgrade package.~n"
- "~n"
- "Valid command line options:~n"
- " previous_release=path~n"
- " target_dir=target_dir (optional)~n",
- []).
-
-run_checks(Config, OldVerPath, ReltoolConfig) ->
- true = rebar_utils:prop_check(filelib:is_dir(OldVerPath),
- "Release directory doesn't exist (~p)~n",
- [OldVerPath]),
-
- {Name, Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
-
- NewVerPath = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
- true = rebar_utils:prop_check(filelib:is_dir(NewVerPath),
- "Release directory doesn't exist (~p)~n",
- [NewVerPath]),
-
- {NewName, NewVer} = rebar_rel_utils:get_rel_release_info(Name, NewVerPath),
- {OldName, OldVer} = rebar_rel_utils:get_rel_release_info(Name, OldVerPath),
-
- true =
- rebar_utils:prop_check(NewName == OldName,
- "New and old .rel release names do not match~n",
- []),
- true =
- rebar_utils:prop_check(Name == NewName,
- "Reltool and .rel release names do not match~n",
- []),
- true =
- rebar_utils:prop_check(NewVer =/= OldVer,
- "New and old .rel contain the same version~n",
- []),
- true =
- rebar_utils:prop_check(Ver == NewVer,
- "Reltool and .rel versions do not match~n", []),
-
- {NewVerPath, NewName, NewVer}.
-
-setup(OldVerPath, NewVerPath, NewName, NewVer, NameVer) ->
- Src = filename:join([NewVerPath, "releases",
- NewVer, NewName ++ ".rel"]),
- Dst = filename:join([".", NameVer ++ ".rel"]),
- {ok, _} = file:copy(Src, Dst),
- ok = code:add_pathsa(
- lists:append([
- filelib:wildcard(filename:join([NewVerPath,
- "lib", "*", "ebin"])),
- filelib:wildcard(filename:join([OldVerPath,
- "releases", "*"])),
- filelib:wildcard(filename:join([OldVerPath,
- "lib", "*", "ebin"]))
- ])).
-
-run_systools(NewVer, Name) ->
- Opts = [silent],
- NameList = [Name],
- case systools:make_relup(NewVer, NameList, NameList, Opts) of
- {error, _, Msg} ->
- ?ABORT("Systools [systools:make_relup/4] aborted with: ~p~n",
- [Msg]);
- _ ->
- ?DEBUG("Relup created~n", []),
- case systools:make_script(NewVer, Opts) of
- {error, _, Msg1} ->
- ?ABORT("Systools [systools:make_script/2] "
- "aborted with: ~p~n", [Msg1]);
- _ ->
- ?DEBUG("Script created~n", []),
- case systools:make_tar(NewVer, Opts) of
- {error, _, Msg2} ->
- ?ABORT("Systools [systools:make_tar/2] "
- "aborted with: ~p~n", [Msg2]);
- _ ->
- ?DEBUG("Tarball created~n", []),
- ok
- end
- end
- end.
-
-boot_files(TargetDir, Ver, Name) ->
- ok = file:make_dir(filename:join([".", ?TMP])),
- ok = file:make_dir(filename:join([".", ?TMP, "releases"])),
- ok = file:make_dir(filename:join([".", ?TMP, "releases", Ver])),
- case os:type() of
- {win32,_} ->
- ok;
- _ ->
- ok = file:make_symlink(
- filename:join(["start.boot"]),
- filename:join([".", ?TMP, "releases", Ver, Name ++ ".boot"]))
- end,
- {ok, _} =
- file:copy(
- filename:join([TargetDir, "releases", Ver, "start_clean.boot"]),
- filename:join([".", ?TMP, "releases", Ver, "start_clean.boot"])),
-
- SysConfig = filename:join([TargetDir, "releases", Ver, "sys.config"]),
- _ = case filelib:is_regular(SysConfig) of
- true ->
- {ok, _} = file:copy(
- SysConfig,
- filename:join([".", ?TMP, "releases", Ver,
- "sys.config"]));
- false -> ok
- end,
-
- VmArgs = filename:join([TargetDir, "releases", Ver, "vm.args"]),
- case filelib:is_regular(VmArgs) of
- true ->
- {ok, _} = file:copy(
- VmArgs,
- filename:join([".", ?TMP, "releases", Ver, "vm.args"]));
- false -> {ok, 0}
- end.
-
-make_tar(NameVer, NewVer, NewName) ->
- Filename = NameVer ++ ".tar.gz",
- {ok, Cwd} = file:get_cwd(),
- Absname = filename:join([Cwd, Filename]),
- ok = file:set_cwd(?TMP),
- ok = erl_tar:extract(Absname, [compressed]),
- ok = file:delete(Absname),
- case os:type() of
- {win32,_} ->
- {ok, _} =
- file:copy(
- filename:join([".", "releases", NewVer, "start.boot"]),
- filename:join([".", "releases", NewVer, NewName ++ ".boot"])),
- ok;
- _ ->
- ok
- end,
- {ok, Tar} = erl_tar:open(Absname, [write, compressed]),
- ok = erl_tar:add(Tar, "lib", []),
- ok = erl_tar:add(Tar, "releases", []),
- ok = erl_tar:close(Tar),
- ok = file:set_cwd(Cwd),
- ?CONSOLE("~s upgrade package created~n", [NameVer]).
-
-cleanup(NameVer) ->
- ?DEBUG("Removing files needed for building the upgrade~n", []),
- Files = [
- filename:join([".", NameVer ++ ".rel"]),
- filename:join([".", NameVer ++ ".boot"]),
- filename:join([".", NameVer ++ ".script"]),
- filename:join([".", "relup"])
- ],
- lists:foreach(fun(F) -> ok = file:delete(F) end, Files),
-
- ok = remove_dir_tree(?TMP).
-
-%% adapted from http://www.erlang.org/doc/system_principles/create_target.html
-remove_dir_tree(Dir) ->
- remove_all_files(".", [Dir]).
-remove_all_files(Dir, Files) ->
- lists:foreach(fun(File) ->
- FilePath = filename:join([Dir, File]),
- {ok, FileInfo, Link} = file_info(FilePath),
- case {Link, FileInfo#file_info.type} of
- {false, directory} ->
- {ok, DirFiles} = file:list_dir(FilePath),
- remove_all_files(FilePath, DirFiles),
- file:del_dir(FilePath);
- _ ->
- file:delete(FilePath)
- end
- end, Files).
-
-file_info(Path) ->
- case file:read_file_info(Path) of
- {ok, Info} ->
- {ok, Info, false};
- {error, enoent} ->
- {ok, Info} = file:read_link_info(Path),
- {ok, Info, true};
- Error ->
- Error
- end.
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index f3a082d..8474a26 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -36,7 +36,7 @@
find_files/3,
now_str/0,
ensure_dir/1,
- beam_to_mod/2,
+ beam_to_mod/1,
beams/1,
erl_to_mod/1,
abort/0,
@@ -503,9 +503,8 @@ sh_loop(Port, Fun, Acc) ->
{error, {Rc, lists:flatten(lists:reverse(Acc))}}
end.
-beam_to_mod(Dir, Filename) ->
- [Dir | Rest] = filename:split(Filename),
- list_to_atom(filename:basename(string:join(Rest, "."), ".beam")).
+beam_to_mod(Filename) ->
+ list_to_atom(filename:basename(Filename, ".beam")).
erl_to_mod(Filename) ->
list_to_atom(filename:rootname(filename:basename(Filename))).