| %% -*- 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_app_utils). |
| |
| -export([is_app_dir/0, |
| is_app_dir/1, |
| is_app_src/1, |
| app_src_to_app/1, |
| app_name/2, |
| app_applications/2, |
| app_vsn/2, |
| is_skipped_app/2]). |
| |
| -export([load_app_file/2]). % TEMPORARY |
| |
| -include("rebar.hrl"). |
| |
| %% =================================================================== |
| %% Public API |
| %% =================================================================== |
| |
| is_app_dir() -> |
| is_app_dir(rebar_utils:get_cwd()). |
| |
| is_app_dir(Dir) -> |
| SrcDir = filename:join([Dir, "src"]), |
| AppSrcScript = filename:join([SrcDir, "*.app.src.script"]), |
| AppSrc = filename:join([SrcDir, "*.app.src"]), |
| case {filelib:wildcard(AppSrcScript), filelib:wildcard(AppSrc)} of |
| {[AppSrcScriptFile], _} -> |
| {true, AppSrcScriptFile}; |
| {[], [AppSrcFile]} -> |
| {true, AppSrcFile}; |
| {[],[]} -> |
| EbinDir = filename:join([Dir, "ebin"]), |
| App = filename:join([EbinDir, "*.app"]), |
| case filelib:wildcard(App) of |
| [AppFile] -> |
| {true, AppFile}; |
| [] -> |
| false; |
| _ -> |
| ?ERROR("More than one .app file in ~s~n", [EbinDir]), |
| false |
| end; |
| {_, _} -> |
| ?ERROR("More than one .app.src file in ~s~n", [SrcDir]), |
| false |
| end. |
| |
| |
| is_app_src(Filename) -> |
| %% If removing the extension .app.src yields a shorter name, |
| %% this is an .app.src file. |
| Filename =/= filename:rootname(Filename, ".app.src") orelse |
| Filename =/= filename:rootname(Filename, ".app.src.script"). |
| |
| app_src_to_app(Filename) -> |
| Filebase = case filename:rootname(Filename, ".app.src") of |
| Filename -> |
| filename:basename(Filename, ".app.src.script"); |
| _ -> |
| filename:basename(Filename, ".app.src") |
| end, |
| filename:join("ebin", Filebase ++ ".app"). |
| |
| app_name(Config, AppFile) -> |
| case load_app_file(Config, AppFile) of |
| {ok, NewConfig, AppName, _} -> |
| {NewConfig, AppName}; |
| {error, Reason} -> |
| ?ABORT("Failed to extract name from ~s: ~p\n", |
| [AppFile, Reason]) |
| end. |
| |
| app_applications(Config, AppFile) -> |
| case load_app_file(Config, AppFile) of |
| {ok, NewConfig, _, AppInfo} -> |
| {NewConfig, get_value(applications, AppInfo, AppFile)}; |
| {error, Reason} -> |
| ?ABORT("Failed to extract applications from ~s: ~p\n", |
| [AppFile, Reason]) |
| end. |
| |
| app_vsn(Config, AppFile) -> |
| case load_app_file(Config, AppFile) of |
| {ok, Config1, _, AppInfo} -> |
| AppDir = filename:dirname(filename:dirname(AppFile)), |
| rebar_utils:vcs_vsn(Config1, get_value(vsn, AppInfo, AppFile), |
| AppDir); |
| {error, Reason} -> |
| ?ABORT("Failed to extract vsn from ~s: ~p\n", |
| [AppFile, Reason]) |
| end. |
| |
| is_skipped_app(Config, AppFile) -> |
| {Config1, ThisApp} = app_name(Config, AppFile), |
| %% Check for apps global parameter; this is a comma-delimited list |
| %% of apps on which we want to run commands |
| Skipped = |
| case get_apps(Config) of |
| undefined -> |
| %% No apps parameter specified, check the skip_apps list.. |
| case get_skip_apps(Config) of |
| undefined -> |
| %% No skip_apps list, run everything.. |
| false; |
| SkipApps -> |
| TargetApps = [list_to_atom(A) || |
| A <- string:tokens(SkipApps, ",")], |
| is_skipped(ThisApp, TargetApps) |
| end; |
| Apps -> |
| %% run only selected apps |
| TargetApps = [list_to_atom(A) || A <- string:tokens(Apps, ",")], |
| is_selected(ThisApp, TargetApps) |
| end, |
| {Config1, Skipped}. |
| |
| %% =================================================================== |
| %% Internal functions |
| %% =================================================================== |
| |
| load_app_file(Config, Filename) -> |
| AppFile = {app_file, Filename}, |
| case rebar_config:get_xconf(Config, {appfile, AppFile}, undefined) of |
| undefined -> |
| case consult_app_file(Filename) of |
| {ok, {application, AppName, AppData}} -> |
| Config1 = rebar_config:set_xconf(Config, |
| {appfile, AppFile}, |
| {AppName, AppData}), |
| {ok, Config1, AppName, AppData}; |
| {error, _} = Error -> |
| {error, {error, Error}}; |
| Other -> |
| {error, {unexpected_terms, Other}} |
| end; |
| {AppName, AppData} -> |
| {ok, Config, AppName, AppData} |
| end. |
| |
| %% In the case of *.app.src we want to give the user the ability to |
| %% dynamically script the application resource file (think dynamic version |
| %% string, etc.), in a way similar to what can be done with the rebar |
| %% config. However, in the case of *.app, rebar should not manipulate |
| %% that file. This enforces that dichotomy between app and app.src. |
| consult_app_file(Filename) -> |
| Result = case lists:suffix(".app", Filename) of |
| true -> |
| file:consult(Filename); |
| false -> |
| rebar_config:consult_file(Filename) |
| end, |
| case Result of |
| {ok, [Term]} -> |
| {ok, Term}; |
| _ -> |
| Result |
| end. |
| |
| get_value(Key, AppInfo, AppFile) -> |
| case proplists:get_value(Key, AppInfo) of |
| undefined -> |
| ?ABORT("Failed to get app value '~p' from '~s'~n", [Key, AppFile]); |
| Value -> |
| Value |
| end. |
| |
| %% apps= for selecting apps |
| is_selected(ThisApp, TargetApps) -> |
| case lists:member(ThisApp, TargetApps) of |
| false -> |
| {true, ThisApp}; |
| true -> |
| false |
| end. |
| |
| %% skip_apps= for filtering apps |
| is_skipped(ThisApp, TargetApps) -> |
| case lists:member(ThisApp, TargetApps) of |
| false -> |
| false; |
| true -> |
| {true, ThisApp} |
| end. |
| |
| get_apps(Config) -> |
| rebar_config:get_global(Config, apps, undefined). |
| |
| get_skip_apps(Config) -> |
| rebar_config:get_global(Config, skip_apps, undefined). |