Skip to content
Browse files

support version control based vsn in app.src

  • Loading branch information...
1 parent b2fe2e1 commit 3fe1395c8d9c000ae1471c663ff8be50d9917458 @ericbmerritt committed May 15, 2012
View
24 include/sinan.hrl
@@ -14,8 +14,25 @@
path :: string(),
type :: runtime | compiletime,
project :: boolean(),
- modules :: sin_file_info:mod(),
- sources :: sin_file_info:mod()}).
+ %% Everything below will only be populated if the
+ %% project field above is true otherwise they will be
+ %% undefined.
+ id="" :: string() | list(),
+ maxP=infinity :: integer() | infinity,
+ maxT=infinity :: integer() | infinity,
+ env=[] :: list(),
+ start_phases = undefined :: term() | undefined,
+ basedir :: string() | undefined,
+ description="" :: string() | list(),
+ registered :: [atom()] | undefined,
+ applications :: [atom()] | undefined,
+ included_applications :: [atom()] | undefined,
+ dep_constraints :: term() | undefined,
+ mod :: term() | undefined,
+ dotapp :: string() | undefined,
+ deps :: [atom()] | undefined,
+ properties=[] :: proplists:proplist(),
+ modules :: [atom()] | undefined}).
%% @doc describes a module in the system
%% - name: is the name of the module
@@ -24,7 +41,8 @@
path :: string(),
module_deps :: [module()],
includes :: [atom()],
- include_timestamps :: [{string(), sin_file_info:date_time()}],
+ include_timestamps :: [{string(),
+ sin_file_info:date_time()}],
tags :: [atom()],
called_modules :: [atom()],
changed :: sin_file_info:date_time(),
View
202 src/sin_app_meta.erl
@@ -12,66 +12,178 @@
-include_lib("sinan/include/sinan.hrl").
%% API
--export([populate/2,
- rewrite_vsn/4,
+-export([build_meta/3,
+ populate_modules/1,
+ write_app_file/1,
format_exception/1]).
+%% SLASH_RE was create by doing re:compile("\\/").
+-define(SLASH_RE,
+ {re_pattern,0,0,
+ <<69,82,67,80,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,0,5,27,47,83,0,5,27,92,
+ 84,0,10,0>>}).
+
%%====================================================================
%% API
%%====================================================================
-
-%% @doc do the system preparation
--spec populate(sin_config:matcher(), sin_state:state()) -> sin_state:state().
-populate(Config, State0) ->
- ProjectApps = sin_state:get_value(project_apps, State0),
- lists:foldl(fun(App, State1) ->
- prepare_app(Config, State1, App)
- end, State0, ProjectApps).
-
-rewrite_vsn(Config, State, AppDir, BaseDetails) ->
- RWVSN = fun({vsn, VsnSpec}) ->
- {vsn, vcs_vsn(Config, State, VsnSpec, AppDir)};
- (El) -> El
- end,
- [RWVSN(Detail) || Detail <- BaseDetails].
+write_app_file(#app{name=AppName,vsn=Vsn,path=Path,
+ description=Desc,registered=Reg,
+ applications=Appls,included_applications=IA,
+ mod=Mod,modules=AppModules,
+ id=Id,maxP=MaxP,maxT=MaxT,env=Env,
+ start_phases=StartPhases}) ->
+ MetaDataPath = filename:join([Path, "ebin",
+ erlang:atom_to_list(AppName) ++
+ ".app"]),
+ MetaData = {application, AppName,
+ [{description, Desc},
+ {id, Id},
+ {vsn, Vsn},
+ {modules, AppModules},
+ {maxP, MaxP},
+ {maxT, MaxT},
+ {registered, Reg},
+ {included_applications, IA},
+ {applications, Appls},
+ {env, Env},
+ {mod, Mod},
+ {start_phases, StartPhases}]},
+ file:write_file(MetaDataPath,
+ io_lib:format("~p.\n",
+ [MetaData])).
+
+populate_modules(App=#app{path=Path}) ->
+ EbinDir = filename:join(Path, "ebin"),
+ Modules =
+ filelib:fold_files(EbinDir,
+ ".+\.beam",
+ true,
+ fun(File, Acc) ->
+ PathName = filename:rootname(File, ".beam"),
+ Name = string:substr(PathName,
+ erlang:length(EbinDir) + 2),
+ ModuleName =
+ erlang:list_to_atom(erlang:binary_to_list(
+ erlang:iolist_to_binary(
+ re:replace(Name,
+ ?SLASH_RE,
+ ".",
+ [global])))),
+ [ModuleName | Acc]
+ end,
+ []),
+ App#app{modules=Modules}.
+
+build_meta(Config, State0, AppDir) ->
+ AppFile = get_app_file(State0, AppDir),
+ case file:consult(AppFile) of
+ {ok, [Detail={application, _, _Details}]} ->
+ build_out_app_description(Config, State0, AppDir, AppFile, Detail);
+ {error, {_, Module, Desc}} ->
+ Error = Module:format_error(Desc),
+ ?SIN_RAISE(State0, {invalid_app_file, AppFile, Error});
+ {error, Error} ->
+ ?SIN_RAISE(State0,
+ {no_app_file_available, AppFile, Error})
+ end.
%% @doc Format an exception thrown by this module
-spec format_exception(sin_exceptions:exception()) ->
- string().
+ string().
+format_exception(?SIN_EXEP_UNPARSE(_, {unable_to_read_config,
+ AppFilePath, Error})) ->
+ io_lib:format("unable parse *.app file ~s:~s.",
+ [AppFilePath, file:format_error(Error)]);
+format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, bad_name,
+ AppFile, AppName, WrongAppName})) ->
+ io_lib:format("~s file has wrong app name. Should be ~p,"
+ " but is ~p.", [AppFile, AppName, WrongAppName]);
+format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, AppFile,
+ _AppName, Error})) ->
+ io_lib:format("Unable to parse *.app file ~s due to ~p.",
+ [AppFile, Error]);
+format_exception(?SIN_EXEP_UNPARSE(_, {no_app_file_available,
+ AppFile, _AppName, Error})) ->
+ io_lib:format("Unable to access file ~s due to ~p.", [AppFile, Error]);
format_exception(Exception) ->
sin_exceptions:format_exception(Exception).
%%====================================================================
%% Internal functions
%%====================================================================
--spec prepare_app(sin_config:config(), sin_state:state(), string()) ->
- sin_config:config().
-prepare_app(_Config, State0, #app{name=AppName, path=AppBuildDir}) ->
- BaseDetails = populate_modules(State0, AppName),
- DotApp = filename:join([AppBuildDir, "ebin",
- erlang:atom_to_list(AppName) ++ ".app"]),
-
- ok = file:write_file(DotApp,
- io_lib:format("~p.\n",
- [{application, AppName, BaseDetails}])),
-
- State0.
-
-populate_modules(State, AppName) ->
- Details = sin_state:get_value({apps, AppName, base}, State),
-
- SourceModuleList = lists:map(fun({_, Module, _, _, _}) ->
- Module
- end,
- sin_state:get_value({apps, AppName, src_modules_detail},
- State)),
+rewrite_vsn(Config, State, AppDir, BaseDetails) ->
+ RWVSN = fun({vsn, VsnSpec}) ->
+ {vsn, vcs_vsn(Config, State, VsnSpec, AppDir)};
+ (El) -> El
+ end,
+ [RWVSN(Detail) || Detail <- BaseDetails].
- lists:reverse(
- lists:foldl(fun(Element = {vsn, _}, Acc) ->
- [{modules, SourceModuleList}, Element | Acc];
- (Element, Acc) ->
- [Element | Acc]
- end, [], lists:keydelete(modules, 1, Details))).
+build_out_app_description(Config, State,
+ AppDir, AppFile,
+ {application, AppName, Details0}) ->
+ Details1 = rewrite_vsn(Config, State, AppDir, Details0),
+ Applications =
+ case proplists:get_value(applications, Details1) of
+ undefined ->
+ [];
+ AppList ->
+ AppList
+ end,
+ IncludedApps =
+ case proplists:get_value(included_applications, Details1) of
+ undefined ->
+ [];
+ IncAppList ->
+ IncAppList
+ end,
+ DepConstraints =
+ case proplists:get_value(dep_constraints, Details1) of
+ undefined ->
+ [];
+ DepCons ->
+ DepCons
+ end,
+ Vsn = proplists:get_value(vsn, Details1),
+ #app{name=AppName,
+ vsn=Vsn,
+ id = proplists:get_value(id, Details1, ""),
+ maxP = proplists:get_value(maxP, Details1, infinity),
+ maxT = proplists:get_value(maxT, Details1, infinity),
+ env = proplists:get_value(env, Details1, []),
+ start_phases = proplists:get_value(start_phases,
+ Details1, undefined),
+ basedir = AppDir,
+ description = proplists:get_value(description, Details1),
+ registered =
+ case proplists:get_value(registered, Details1) of
+ undefined ->
+ [];
+ Value ->
+ Value
+ end,
+ applications = Applications,
+ included_applications = IncludedApps,
+ mod = proplists:get_value(mod, Details1),
+ dotapp=AppFile,
+ dep_constraints = DepConstraints,
+ deps=Applications ++ IncludedApps}.
+
+get_app_file(State, AppDir) ->
+ EbinDir = filename:join([AppDir, "ebin"]),
+ SrcDir = filename:join([AppDir, "src"]),
+ case sin_utils:get_file_with_ext(EbinDir, "app") of
+ false ->
+ case sin_utils:get_file_with_ext(SrcDir, "app.src") of
+ false ->
+ %% This shouldn't occur
+ ?SIN_RAISE(State, unable_to_find_app_file);
+ SrcAppFile ->
+ SrcAppFile
+ end;
+ AppFile ->
+ AppFile
+ end.
vcs_vsn(Config, State, Vcs, Dir) ->
case vcs_vsn_cmd(Vcs) of
View
361 src/sin_discover.erl
@@ -45,20 +45,19 @@
%%====================================================================
%% @doc Run the discover task.
--spec discover(sin_config:config(),
- sin_state:state()) ->
- sin_state:state().
-discover(Config0, State0) ->
- ProjectDir = find_project_root(State0, sin_state:get_value(start_dir, State0)),
- {Config1, State1}
- = process_raw_config(ProjectDir,
- find_config(ProjectDir, Config0, State0),
- Config0,
- State0),
- {AppDirs, State2} = look_for_app_dirs(Config1, State1,
- sin_state:get_value(build_dir, State1),
- ProjectDir),
- {Config1, build_app_info(State2, AppDirs, [])}.
+discover(CmdLineConfig, State0) ->
+ ProjectDir =
+ find_project_root(State0, sin_state:get_value(start_dir, State0)),
+ {Config, State1} = create_base_config(CmdLineConfig, State0, ProjectDir),
+ {AppDirs, State2} =
+ look_for_app_dirs(Config, State1, ProjectDir),
+ Apps = [sin_app_meta:build_meta(Config, State2, AppDir)
+ || AppDir <- AppDirs],
+
+ populate_config_and_state(ProjectDir, Config,
+ sin_state:store(project_applist, Apps, State2),
+ Apps).
+
%% @doc Format an exception thrown by this module
-spec format_exception(sin_exceptions:exception()) ->
@@ -68,21 +67,6 @@ format_exception(?SIN_EXEP_UNPARSE(_, {ebin_src_conflict, Dir})) ->
"and a src/*.app.src.", [Dir]);
format_exception(?SIN_EXEP_UNPARSE(_, unable_to_intuit_config)) ->
"Unable to generate a project configuration from available metadata.";
-format_exception(?SIN_EXEP_UNPARSE(_, {unable_to_read_config,
- AppFilePath, Error})) ->
- io_lib:format("unable parse *.app file ~s:~s.",
- [AppFilePath, file:format_error(Error)]);
-format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, bad_name,
- AppFile, AppName, WrongAppName})) ->
- io_lib:format("~s file has wrong app name. Should be ~p,"
- " but is ~p.", [AppFile, AppName, WrongAppName]);
-format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, AppFile,
- _AppName, Error})) ->
- io_lib:format("Unable to parse *.app file ~s due to ~p.",
- [AppFile, Error]);
-format_exception(?SIN_EXEP_UNPARSE(_, {no_app_file_available,
- AppFile, _AppName, Error})) ->
- io_lib:format("Unable to access file ~s due to ~p.", [AppFile, Error]);
format_exception(?SIN_EXEP_UNPARSE(_, {no_app_directories,
ProjectDir})) ->
io_lib:format("Unable to find any application directories "
@@ -94,133 +78,90 @@ format_exception(Exception) ->
%%% Internal functions
%%====================================================================
+create_base_config(CommandLineConfig, State0, ProjectDir) ->
+ HomeDir = get_home_dir(CommandLineConfig),
+ State1 = sin_state:store(home_dir, HomeDir, State0),
+ {FileConfig, State2} = get_file_config(ProjectDir, State1),
+ DefaultConfig =
+ sin_config:new_from_terms(default_config_terms(), []),
+ HomeConfig = get_home_config(State2, HomeDir),
+ {sin_config:merge(CommandLineConfig,
+ sin_config:merge(FileConfig,
+ sin_config:merge(HomeConfig,
+ DefaultConfig))),
+ State2}.
+
%% @doc This trys to find the build config if it can. If the config is found it
%% is parsed in the normal way and returned to the user. however, if it is not
+
%% found then the system attempts to unuit the build config from the project
%% properties.
--spec find_config(string(),
- sin_config:config(), sin_state:state()) -> sin_config:config().
-find_config(ProjectDir, Config, State) ->
+get_file_config(ProjectDir, State) ->
ConfigPath = filename:join([ProjectDir, ?CONFIG_NAME]),
try
- sin_config:new_from_file(ConfigPath, [])
+ {sin_config:new_from_file(ConfigPath, []),
+ sin_state:store(intuited_config, false, State)}
catch
throw:{error_accessing_file, ConfigPath, enoent} ->
- intuit_build_config(ProjectDir, Config, State)
+ {sin_config:new(), sin_state:store(intuited_config, true, State)}
end.
%% @doc Take a raw config and merge it with the default config information
--spec process_raw_config(string(),
- sin_config:config(),
- sin_config:config(),
- sin_state:state()) ->
- sin_config:config().
-process_raw_config(ProjectDir, FileConfig, CommandLineConfig, State0) ->
- HomeDir = get_home_dir(CommandLineConfig),
- DefaultConfig =
- sin_config:new_from_terms(default_config_terms(), []),
- HomeConfig = get_home_config(State0, HomeDir),
- Config0 =
- sin_config:merge(CommandLineConfig,
- sin_config:merge(FileConfig,
- sin_config:merge(HomeConfig,
- DefaultConfig))),
- BuildDir = sin_config:match(build_dir, Config0),
+populate_config_and_state(ProjectDir, Config0, State0, Apps) ->
+ Config1 = case sin_state:get_value(intuited_config, false, State0) of
+ false ->
+ Config0;
+ true ->
+ intuit_build_config(Config0, State0, Apps)
+ end,
+ BuildDir = sin_config:match(build_dir, Config1),
ReleaseDir = filename:join([ProjectDir,
BuildDir,
- get_release_name(Config0)]),
- State1 = sin_state:store([{release, get_release_name(Config0)},
- {release_vsn, sin_config:match(project_vsn, Config0)},
+ get_release_name(Config1)]),
+ State1 = sin_state:store([{release, get_release_name(Config1)},
+ {release_vsn, sin_config:match(project_vsn, Config1)},
{build_root, filename:join([ProjectDir,
BuildDir])},
{build_dir, ReleaseDir},
{apps_dir, filename:join(ReleaseDir, "lib")},
{release_dir, filename:join(ReleaseDir, "releases")},
- {home_dir, HomeDir},
{project_dir, ProjectDir}], State0),
-
-
{Config0, State1}.
%% @doc If this is a single app project then the config is generated from the
%% app name and version combined with the usual default config information. If
%% this is not a single app project then a build config cannot be intuited and
%% an exception is thrown.
--spec intuit_build_config(string(),
- sin_config:config(),
- sin_state:state()) ->
- sin_config:config().
-intuit_build_config(ProjectDir, Config, State) ->
- try
- AppFilePath = get_app_file(ProjectDir),
- case file:consult(AppFilePath) of
- {error, enoent} ->
- ?SIN_RAISE(State, unable_to_intuit_config);
- {error, Error} ->
- ?SIN_RAISE(State, {unable_to_read_config,
- AppFilePath, Error});
- {ok, [All={application, _, _}]} ->
- build_out_intuited_config(All)
- end
- catch
- throw:no_app_metadata_at_top_level ->
- intuit_from_command_line(Config, State)
- end.
-
-
-%% @doc the app file could be in ebin or src/app.src. we need to
-%% check for both
--spec get_app_file(string()) ->
- string().
-get_app_file(ProjectDir) ->
- EbinDir = filename:join(ProjectDir,
- "ebin"),
- SrcDir = filename:join(ProjectDir,
- "src"),
- EbinPath = filelib:fold_files(EbinDir, "(.+)\.app", false,
- fun(BaseName, Acc) ->
- [filename:join(EbinDir, BaseName) | Acc]
- end, []),
- SrcPath = filelib:fold_files(SrcDir, "(.+)\.app.src", false,
- fun(BaseName, Acc) ->
- [filename:join(EbinDir, BaseName) | Acc]
- end, []),
- case {EbinPath, SrcPath} of
- {_, [RSrcDir]} ->
- RSrcDir;
- {[REbinDir], _} ->
- REbinDir;
- _ ->
- throw(no_app_metadata_at_top_level)
+intuit_build_config(Config0, State, Apps) ->
+ case intuit_from_command_line(Config0) of
+ undefined ->
+ case Apps of
+ [#app{name=AppName,vsn=AppVsn}] ->
+ sin_config:add_all([{project_name, AppName},
+ {project_vsn, AppVsn}], Config0);
+ _ ->
+ ?SIN_RAISE(State, unable_to_intuit_config)
+ end;
+ Config1 ->
+ Config1
end.
-%% @doc Given information from the app dir that was found, create a new full
-%% populated correct build config.
--spec build_out_intuited_config(All::{application, atom(), [{atom(), term()}]}) ->
- sin_config:config().
-build_out_intuited_config({application, AppName, Rest}) ->
- {value, {vsn, ProjectVsn}} = lists:keysearch(vsn, 1, Rest),
- sin_config:add_all([{project_name, AppName},
- {project_vsn, ProjectVsn}], sin_config:new()).
-
%% @doc The override is command line override values that are provided by the
%% user. If the user provides a project name and version we can use that
--spec intuit_from_command_line(sin_config:config(), sin_state:state()) ->
- sin_config:config().
-intuit_from_command_line(Config, State) ->
+intuit_from_command_line(Config) ->
+ %% These values are already in the config since they are populated
+ %% automatically. We are just going to make sure they are actually there.
case sin_config:match(project_name, undefined, Config) of
undefined ->
- ?SIN_RAISE(State, unable_to_intuit_config);
+ undefined;
_PName ->
- ok
- end,
- case sin_config:match(project_vsn, undefined, Config) of
- undefined ->
- ?SIN_RAISE(State, unable_to_intuit_config);
- _PVsn ->
- ok
- end,
- Config.
+ case sin_config:match(project_vsn, undefined, Config) of
+ undefined ->
+ undefined;
+ _PVsn ->
+ Config
+ end
+ end.
%% @doc Find the root of the project. The project root may be marked by a _build
%% dir, a build config (sinan.cfg, _build.cfg). If none of those are found then
@@ -289,88 +230,15 @@ parent_dir([_H], Acc) ->
parent_dir([H | T], Acc) ->
parent_dir(T, [H | Acc]).
-%% @doc Given a list of app dirs retrieves the application names.
--spec build_app_info(sin_state:state(),
- List::list(),
- Acc::list()) ->
- Config::sin_config:config().
-build_app_info(State0, [H|T], Acc) ->
- {AppName, AppFile, AppDir} = get_app_info(H),
- case file:consult(AppFile) of
- {ok, [{application, AppName, Details}]} ->
-
- Applications = case lists:keyfind(applications, 1, Details) of
- {applications, AppList} ->
- AppList;
- false ->
- []
- end,
- IncludedApps = case lists:keyfind(included_applications, 1, Details) of
- {included_applications, IncAppList} ->
- IncAppList;
- false ->
- []
- end,
-
- State1 = source_details(AppDir, AppName,
- sin_state:store([{{apps, AppName, dotapp}, AppFile},
- {{apps, AppName, basedir}, AppDir},
- {{apps, AppName, deps}, Applications ++ IncludedApps}],
- State0)),
- State3 =
- lists:foldl(fun({Key, Value}, State4) ->
- sin_state:store({apps, AppName, Key}, Value, State4)
- end,
- sin_state:store({apps, AppName, base},
- Details, State1),
- Details),
-
- build_app_info(State3, T, [AppName | Acc]);
- {ok, [{application, WrongAppName, _Details}]} ->
- ?SIN_RAISE(State0,
- {invalid_app_file, bad_name,
- AppFile, AppName, WrongAppName});
-
- {error, {_, Module, Desc}} ->
- Error = Module:format_error(Desc),
- ?SIN_RAISE(State0, {invalid_app_file, AppFile, AppName, Error});
- {error, Error} ->
- ?SIN_RAISE(State0,
- {no_app_file_available, AppFile, AppName, Error})
- end;
-build_app_info(State, [], Acc) ->
- sin_state:store(project_applist, Acc, State).
-
--spec get_app_info({ebin | appsrc, string()}) -> {string(), string()}.
-get_app_info({ebin, Dir, AppName}) ->
- AppFile = filename:join([Dir, "ebin", string:concat(AppName, ".app")]),
- {erlang:list_to_atom(AppName), AppFile, Dir};
-get_app_info({appsrc, Dir, AppName}) ->
- AppFile = filename:join([Dir, "src", string:concat(AppName, ".app.src")]),
- {erlang:list_to_atom(AppName), AppFile, Dir}.
-
--spec source_details(string(), string(), sin_config:config()) ->
- sin_config:config().
-source_details(Dir, AppName, State0) ->
- SrcDir = filename:join([Dir, "src"]),
- TestDir = filename:join([Dir, "test"]),
-
- SrcModules = gather_modules(SrcDir),
- TestModules = gather_modules(TestDir),
- sin_state:store({apps, AppName, src_modules_detail}, SrcModules,
- sin_state:store({apps, AppName, all_modules_detail},
- SrcModules ++ TestModules, State0)).
-
%% @doc Rolls through subdirectories of the build directory looking for
%% directories that have a src and an ebin subdir. When it finds one it stops
%% recursing and adds the directory to the list to return.
-spec look_for_app_dirs(sin_config:config(),
sin_state:state(),
- BuildDir::string(),
ProjectDir::string()) ->
AppDirs::[string()].
-look_for_app_dirs(Config, State0, BuildDir, ProjectDir) ->
- Ignorables = sin_config:match(ignore_dirs, Config) ++ [BuildDir],
+look_for_app_dirs(Config, State0, ProjectDir) ->
+ Ignorables = sin_config:match(ignore_dirs, Config),
State1 = sin_state:store(ignore_dirs, Ignorables, State0),
case process_possible_app_dir(State1, ProjectDir,
Ignorables, []) of
@@ -389,106 +257,51 @@ process_possible_app_dir(State, TargetDir, Ignorables, Acc) ->
case filelib:is_dir(TargetDir) andalso not
sin_utils:is_dir_ignorable(TargetDir, Ignorables) of
true ->
- {ok, Dirs} = file:list_dir(TargetDir),
- Res = has_src(State, TargetDir, Dirs),
- case Res of
- Val = {Type, TargetDir, _} when Type =:= appsrc;
- Type =:= ebin ->
- [Val | Acc];
-
- _ ->
+ {ok, SubDirs} = file:list_dir(TargetDir),
+ case is_app_directory(TargetDir, SubDirs) of
+ true ->
+ [TargetDir | Acc];
+ false ->
lists:foldl(fun(Sub, NAcc) ->
Dir = filename:join([TargetDir, Sub]),
process_possible_app_dir(State,
Dir,
Ignorables,
NAcc)
- end, Acc, Dirs)
+ end, Acc, SubDirs)
end;
false ->
Acc
end.
--spec has_src(sin_state:state(),
- string(), [string()]) ->
+is_app_directory(BaseDir, SubDirs) ->
+ has_ebin(BaseDir, SubDirs) orelse has_src(BaseDir, SubDirs).
+
+-spec has_src(string(), [string()]) ->
false | {appsrc | ebin, string()}.
-has_src(State, BaseDir, SubDirs) ->
+has_src(BaseDir, SubDirs) ->
case lists:member("src", SubDirs) of
true ->
- case has_file(BaseDir, "src", ".app.src") of
- false ->
- has_ebin(State, BaseDir, SubDirs);
- AppName ->
- {appsrc, BaseDir, AppName}
- end;
+ sin_utils:get_file_with_ext(
+ filename:join(BaseDir, "src"), ".app.src") =/= false;
false ->
- has_ebin(State, BaseDir, SubDirs)
+ false
end.
--spec has_ebin(sin_state:state(),
- string(), [string()]) ->
+-spec has_ebin(string(), [string()]) ->
false | {appsrc | ebin, string()}.
-has_ebin(_State, BaseDir, SubDirs) ->
+has_ebin(BaseDir, SubDirs) ->
case lists:member("ebin", SubDirs) of
true ->
- case has_file(BaseDir, "ebin", ".app") of
- false ->
- false;
- AppName ->
- {ebin, BaseDir, AppName}
- end;
+ sin_utils:get_file_with_ext(
+ filename:join(BaseDir, "ebin"), ".app") =/= false;
false ->
false
end.
-has_file(BaseDir, SrcDir, Ext) ->
- case file:list_dir(filename:join(BaseDir, SrcDir)) of
- {ok, Names} ->
- lists:foldl(fun(Name, false) ->
- case filename:basename(Name, Ext) of
- Name ->
- false;
- BaseName ->
- BaseName
- end;
- (_, Acc) ->
- Acc
- end, false, Names);
- _ ->
- false
- end.
-
-%% @doc Gather the list of modules that currently may need to be built.
-gather_modules(SrcDir) ->
- case filelib:is_dir(SrcDir) of
- true ->
- filelib:fold_files(SrcDir,
- "(.+\.erl|.+\.yrl|.+\.asn1|.+\.asn)$",
- true, % Recurse into subdirectories of src
- fun(File, Acc) ->
- Ext = filename:extension(File),
- AtomExt = list_to_atom(Ext),
- TestImplementations =
- case string:str(File, "_SUITE.erl") of
- 0 ->
- [];
- _ ->
- [common_test]
- end,
- [{File, module_name(File), Ext, AtomExt,
- TestImplementations} | Acc]
- end, []);
- false ->
- []
- end.
-
-%% @doc Extract the module name from the file name.
-module_name(File) ->
- list_to_atom(filename:rootname(filename:basename(File))).
-
%% @doc the minimal default configuration info
default_config_terms() ->
[{build_dir, "_build"},
@@ -528,7 +341,3 @@ get_home_config(State, HomeDir) ->
false ->
sin_config:new()
end.
-
-%%====================================================================
-%% tests
-%%====================================================================
View
55 src/sin_fs_resolver.erl
@@ -44,12 +44,19 @@ new(Config, State) ->
{sin_dep_resolver:state(),
[sin_dep_solver:spec()]}.
app_dependencies(RState={PathList, State, ProjectApps}, App, Ver) ->
- case lists:member(App, ProjectApps) of
- true ->
- {Deps, VersionedDeps} = get_app_constraints(State,
- sin_state:get_value({apps, App, dotapp}, State)),
- {RState, Deps ++ VersionedDeps};
- false ->
+ case ec_lists:search(fun(#app{name=AppName,vsn=Vsn,
+ deps=Deps, dep_constraints=DepC}) ->
+ case App == AppName
+ andalso Vsn == Ver of
+ true ->
+ {ok, Deps ++ DepC};
+ _ ->
+ not_found
+ end
+ end, ProjectApps) of
+ {ok, Deps, _} ->
+ {RState, Deps};
+ not_found ->
case look_for_dependency_path(State, PathList, App, Ver) of
{ok, Path} ->
{RState, get_dependency_information(State, Path, App)};
@@ -62,10 +69,17 @@ app_dependencies(RState={PathList, State, ProjectApps}, App, Ver) ->
{sin_dep_resolver:impl(),
[sin_dep_solver:versions()]}.
app_versions(RState={PathList, State, ProjectApps}, App) ->
- case lists:member(App, ProjectApps) of
- true ->
- {RState, [get_app_vsn(State, sin_state:get_value({apps, App, dotapp}, State))]};
- false ->
+ case ec_lists:search(fun(#app{name=AppName,vsn=Vsn}) ->
+ case App == AppName of
+ true ->
+ {ok, Vsn};
+ _ ->
+ not_found
+ end
+ end, ProjectApps) of
+ {ok, Vsn, _} ->
+ {RState, [Vsn]};
+ not_found ->
VsnList = get_available_versions(State, PathList, App),
%% Make sure the elements are unique
{RState, sets:to_list(sets:from_list(VsnList))}
@@ -75,7 +89,9 @@ app_versions(RState={PathList, State, ProjectApps}, App) ->
-spec resolve(sin_dep_resolver:impl(), sin_dep_solver:app(), sin_dep_solver:version()) ->
{sin_dep_resolver:impl(), string()}.
resolve(RState={PathList, State, ProjectApps}, App, Version) ->
- case lists:member(App, ProjectApps) of
+ case lists:any(fun(#app{name=AppName}) ->
+ AppName == App
+ end, ProjectApps) of
true ->
AppsBuildDir = sin_state:get_value(apps_dir, State),
Path =
@@ -224,20 +240,3 @@ get_available_versions(State, PathList, App) ->
{unable_to_access_directory, Error, Path})
end
end, [], PathList).
-
--spec get_app_vsn(sin_state:state(), [string()]) ->
- string().
-get_app_vsn(State, AppConfigPath) ->
- case file:consult(AppConfigPath) of
- {ok, [{application, _AppName, Terms}]} ->
- case lists:keyfind(vsn, 1, Terms) of
- {vsn, Vsn} ->
- Vsn;
- _ ->
- ?SIN_RAISE(State, {app_config_contains_no_version, AppConfigPath})
- end;
- {error, enoent} ->
- ?SIN_RAISE(State, {no_app_config, AppConfigPath});
- {error, SomeOtherError} ->
- ?SIN_RAISE(State, {error_opening_file, AppConfigPath, SomeOtherError})
- end.
View
17 src/sin_state.erl
@@ -16,6 +16,7 @@
get_value/2,
get_value/3,
get_pairs/1,
+ project_app_by_name/2,
delete/2,
add_run_error/3,
get_run_errors/1,
@@ -88,6 +89,22 @@ get_value(Key, DefaultValue, State) ->
delete(Key, State) ->
dict:erase(Key, State).
+project_app_by_name(AppName, State) ->
+ ProjectApps = sin_state:get_value(project_apps, State),
+ case ec_lists:search(fun(App=#app{name=Name}) ->
+ case Name == AppName of
+ true ->
+ {ok, App};
+ _ ->
+ not_found
+ end
+ end, ProjectApps) of
+ {ok, ProjApp, _} ->
+ ProjApp;
+ _ ->
+ not_found
+ end.
+
%% @doc Get the complete state as key,value pairs
-spec get_pairs(state()) -> [{key(), value()}].
get_pairs(State) ->
View
48 src/sin_task_build.erl
@@ -93,8 +93,13 @@ do_task(Config, State0) ->
Apps0 = sin_state:get_value(project_apps, State0),
NApps = reorder_apps_according_to_deps(State0, Apps0),
{State1, Apps1} = build_apps(Config, State0, NApps),
- sin_app_meta:populate(Config, sin_state:store(project_apps, Apps1, State1)).
-
+ Apps2 = [sin_app_meta:populate_modules(App) || App <- Apps1],
+ State2 =
+ sin_state:store(project_apps, Apps2, State1),
+ lists:foreach(fun(App) ->
+ sin_app_meta:write_app_file(App)
+ end, Apps2),
+ State2.
%% @doc Gather up all the errors and warnings for output.
-spec gather_fail_info([term()], string()) ->
@@ -151,9 +156,7 @@ format_exception(Exception) ->
[sinan:app()]) -> list().
reorder_apps_according_to_deps(State, AllApps) ->
ReOrdered = lists:foldr(
- fun (#app{name=AppName}, Acc) ->
- AllDeps = sin_state:get_value({apps, AppName, deps},
- State),
+ fun (#app{name=AppName, deps=AllDeps}, Acc) ->
case map_deps(AppName, AllDeps, AllApps) of
[] ->
[{'NONE', AppName} | Acc];
@@ -218,24 +221,30 @@ build_apps(Config, State0, Apps0) ->
Apps3 = lists:reverse(Apps2),
{State2, Apps3}.
-prepare_app(Config, State0, App0 = #app{name=AppName, path=Path}, BuildDir, Ignorables) ->
- AppDir = sin_state:get_value({apps, AppName, basedir}, State0),
+prepare_app(Config, State0,
+ App0 = #app{path=Path,basedir=AppDir},
+ BuildDir, Ignorables) ->
%% Ignore the build dir when copying or we will create a deep monster in a
%% few builds
State1 = sin_utils:copy_dir(Config, State0, Path, AppDir, "",
[BuildDir | Ignorables], []),
- State2 = sin_state:store({apps, AppName, builddir},
- Path, State1),
+
+
+
+
+
+
+
Ebin = filename:join(Path, "ebin"),
ec_file:mkdir_path(Ebin),
true = code:add_patha(Ebin),
- {State3, Mods} =
- process_source_files(State2, AppDir),
- {State3, App0#app{sources=Mods}}.
+ {State2, Mods} =
+ process_source_files(State1, AppDir),
+ {State2, App0#app{properties=[{sources,Mods} | App0#app.properties]}}.
-spec process_source_files(sin_state:state(), atom()) ->
{sin_state:state(), [sinan:mod()]}.
@@ -244,7 +253,6 @@ process_source_files(State0, AppDir) ->
TestDir = filename:join(AppDir, "test"),
IncludeDir = filename:join(AppDir, "include"),
Includes = [SrcDir, TestDir, IncludeDir],
-
{State1, SrcModules} = process_source_files_in_path(State0, SrcDir, Includes),
{State2, TestModules} = process_source_files_in_path(State1, TestDir, Includes),
{State2, SrcModules ++ TestModules}.
@@ -263,22 +271,24 @@ process_source_files_in_path(State0, Dir, Includes) ->
%% @doc Build an individual otp application.
build_app(Config0, State0, App=#app{name=AppName,
path=AppBuildDir,
- sources=Modules0}) ->
+ basedir=AppDir,
+ properties=Properties}) ->
+ Sources = proplists:get_value(sources, Properties),
Config1 = Config0:specialize([{app, AppName}]),
- AppDir = sin_state:get_value({apps, AppName, basedir}, State0),
Target = filename:join([AppBuildDir, "ebin"]),
- {State3, NewModules} =
+ {State3, Modules} =
lists:foldl(fun(Module, {State1, Modules1}) ->
{State2, Mods} =
build_source_if_required(Config1, State1,
Module,
AppDir,
- Target, Modules0),
+ Target, Sources),
{State2, [Mods | Modules1]}
- end, {State0, []}, Modules0),
- {State3, App#app{modules=lists:flatten(NewModules)}}.
+ end, {State0, []}, Sources),
+ {State3, App#app{properties=[{modules, lists:flatten(Modules)} |
+ Properties]}}.
%% @doc go through each source file building with the correct build module.
View
10 src/sin_task_cucumber.erl
@@ -73,7 +73,9 @@ do_task(Config, State) ->
case AdditionalArgs of
["gen", Name, "where", TargetApp] ->
[ {F,
- gen_feature(State, F, TargetApp)}
+ gen_feature(F,
+ sin_utils:find_app_by_name(erlang:list_to_atom(TargetApp),
+ State))}
|| F <- Features, filename:basename(F, ".feature") == Name];
[] ->
[ {F, run_feature(Config, State, F)} || F <- Features ];
@@ -119,11 +121,9 @@ run_feature(Config, State, FeatureFile) ->
?SIN_RAISE(State, {no_implementation, FeatureFile})
end.
-gen_feature(State, FeatureFile, TargetApp) ->
+gen_feature(FeatureFile, #app{basedir=BaseDir}) ->
TargetDir =
- filename:join(
- [sin_state:get_value({apps, erlang:list_to_atom(TargetApp), basedir}, State),
- "test"]),
+ filename:join(BaseDir, "test"),
ok = ec_file:mkdir_path(TargetDir),
ok = cucumberl_gen:gen(FeatureFile, TargetDir).
View
90 src/sin_task_depends.erl
@@ -121,7 +121,6 @@ do_task(Config, State0) ->
true = code:add_patha(Ebin)
end, CompiletimeDeps),
-
MergedDeps = RuntimeDeps0 ++ CompiletimeDeps,
FA = fun (App) ->
@@ -148,8 +147,7 @@ do_task(Config, State0) ->
solve_deps(Config, State0, ProjectApps) ->
DefaultConstraints = Config:match(dep_constraints, []),
- {Apps, ActualSpecs} = remove_excluded(State0,
- ProjectApps,
+ {Apps, ActualSpecs} = remove_excluded(ProjectApps,
DefaultConstraints),
ResolverState0 = sin_dep_resolver:new(sin_fs_resolver, Config, State0),
@@ -162,34 +160,18 @@ solve_deps(Config, State0, ProjectApps) ->
ResolverState1 = sin_dep_solver:extract_resolver_state(SolverState2),
{ReleaseApps1, RuntimeDeps2} =
- lists:foldl(fun({App, Vsn}, {ReleaseApps0, RuntimeDeps1}) ->
- {_, Path} =
- sin_dep_resolver:resolve(ResolverState1,
- App, Vsn),
- case lists:member(App, ProjectApps) of
- true ->
- {[#app{name=App, vsn=Vsn, path=Path,
- type=runtime,
- project=true} | ReleaseApps0],
- RuntimeDeps1};
- false ->
- {ReleaseApps0,
- [#app{name=App, vsn=Vsn, path=Path,
- type=runtime,
- project=false} | RuntimeDeps1]}
- end
- end, {[], []}, RuntimeDeps0),
+ seperate_resolve_deps(ResolverState1, RuntimeDeps0, ProjectApps),
CompiletimeDeps1 =
- lists:foldl(fun({App, Vsn}, Acc) ->
- case in_runtime(App, RuntimeDeps2) of
+ lists:foldl(fun({AppName, Vsn}, Acc) ->
+ case in_runtime(AppName, RuntimeDeps2) of
true ->
Acc;
false ->
{_, Path} =
sin_dep_resolver:resolve(ResolverState1,
- App, Vsn),
- [#app{name=App, vsn=Vsn, path=Path,
+ AppName, Vsn),
+ [#app{name=AppName, vsn=Vsn, path=Path,
type=compiletime, project=false} |
Acc]
end
@@ -203,6 +185,36 @@ solve_deps(Config, State0, ProjectApps) ->
{State1, {ReleaseApps1, RuntimeDeps2, CompiletimeDeps1}}.
+seperate_resolve_deps(ResolverState1, RuntimeDeps0, ProjectApps) ->
+ lists:foldl(fun({AppName, Vsn}, {ReleaseApps0, RuntimeDeps1}) ->
+ {_, Path} =
+ sin_dep_resolver:resolve(ResolverState1,
+ AppName, Vsn),
+ case get_project_app(AppName, ProjectApps) of
+ {ok, PApp, _} ->
+ {[PApp#app{path=Path,
+ type=runtime,
+ project=true} | ReleaseApps0],
+ RuntimeDeps1};
+ not_found ->
+ {ReleaseApps0,
+ [#app{name=AppName, vsn=Vsn, path=Path,
+ type=runtime,
+ project=false} | RuntimeDeps1]}
+ end
+ end, {[], []}, RuntimeDeps0).
+
+get_project_app(AppName, ProjectApps) ->
+ ec_lists:search(fun(PApp=#app{name=PAppName}) ->
+ case PAppName == AppName of
+ true ->
+ {ok,PApp};
+ _ ->
+ not_found
+ end
+ end,
+ ProjectApps).
+
in_runtime(App0, RuntimeDeps) ->
lists:any(fun(#app{name=AppName})
when App0 == AppName ->
@@ -242,12 +254,12 @@ format_exception(Exception) ->
sin_state:state(),
sin_dep_solver:state(),
[sin_dep_solver:spec()]) ->
- {sin_dep_solver:state(), [sin_dep_solver:spec()]}.
+ {sin_dep_solver:state(),
+ [sin_dep_solver:spec()]}.
get_compiletime_deps(Config, State0, SolverState0, DefaultSpecs) ->
CompiletimeApps0 = Config:match(compile_deps, []),
- {CompiletimeApps1, CompileSpecs} = remove_excluded(State0,
- CompiletimeApps0,
+ {CompiletimeApps1, CompileSpecs} = remove_excluded(CompiletimeApps0,
DefaultSpecs),
CompiletimeDeps0 =
case sin_dep_solver:all_deps(Config, SolverState0,
@@ -279,22 +291,12 @@ get_runtime_deps(Config, State0, SolverState0, Apps, Specs) ->
end, Deps0)}
end.
--spec remove_excluded(sin_state:state(),
- [sin_dep_solver:app()],
- [sin_dep_solver:spec()]) ->
- {[sin_dep_solver:app()], [sin_dep_solver:spec()]}.
-remove_excluded(State0, Apps0, Constraints) ->
- Apps1 = lists:filter(fun(App) ->
- not lists:member({exclude, App}, Constraints)
+remove_excluded(Apps0, Constraints) ->
+ Apps1 = lists:filter(fun(#app{name=AppName}) ->
+ not lists:member({exclude, AppName},
+ Constraints)
end, Apps0),
DefaultSpecs =
- lists:map(fun(AppName) ->
- case sin_state:get_value({apps, AppName, vsn},
- State0) of
- undefined ->
- AppName;
- Vsn ->
- {AppName, Vsn}
- end
- end, Apps1),
- {Apps1, DefaultSpecs ++ Constraints}.
+ [{AppName, Vsn} || #app{name=AppName, vsn=Vsn} <- Apps1],
+
+ {[AppName || #app{name=AppName} <- Apps1], DefaultSpecs ++ Constraints}.
View
5 src/sin_task_eqc.erl
@@ -36,8 +36,6 @@ This task runs QuviQ Quick Check on the project, running any eqc tests that it
finds. Note that you *must* have a licensed version of Quick Check installed
on the box for this to work.",
-
-
#task{name = ?TASK,
task_impl = ?MODULE,
bare = false,
@@ -50,7 +48,8 @@ finds. Note that you *must* have a licensed version of Quick Check installed
%% @doc run all tests for all modules in the system
do_task(Config, State0) ->
lists:foldl(
- fun(#app{name=AppName, modules=Modules}, State1) ->
+ fun(#app{name=AppName, properties=Props}, State1) ->
+ Modules = proplists:get_value(modules, Props),
io:format("Quick Check testing app ~p~n", [AppName]),
case Modules == undefined orelse length(Modules) =< 0 of
true ->
View
36 src/sin_task_eunit.erl
@@ -70,32 +70,26 @@ filter_modules(Config, Modules) ->
Modules;
FilterModules ->
[Mod || Mod <- Modules,
- lists:member(atom_to_list(Mod#module.name),
+ lists:member(atom_to_list(Mod),
FilterModules)]
end.
%% @doc Run tests for each module that has a test/0 function
-spec run_module_tests(sin_config:config(),
sin_state:state(), [sin_file_info:mod()]) -> ok.
run_module_tests(Config, State0, AllModules) ->
- lists:foldl(
- fun(#module{name=PreName}, State1) ->
- Name = case PreName of
- {Name0, _} ->
- Name0;
- Name0 ->
- Name0
- end,
- case lists:member({test, 0}, Name:module_info(exports)) of
- true ->
- sin_log:normal(Config, "testing ~p", [Name]),
- case eunit:test(Name, [{verbose, Config:match(verbose, false)}]) of
- error ->
- sin_state:add_run_error(Name, eunit_failure, State1);
- _ ->
- State1
- end;
- _ ->
- State1
- end
+ lists:foldl(fun(Name, State1) ->
+ case lists:member({test, 0}, Name:module_info(exports)) of
+ true ->
+ sin_log:normal(Config, "testing ~p", [Name]),
+ case eunit:test(Name,
+ [{verbose, Config:match(verbose, false)}]) of
+ error ->
+ sin_state:add_run_error(Name, eunit_failure, State1);
+ _ ->
+ State1
+ end;
+ _ ->
+ State1
+ end
end, State0, AllModules).
View
3 src/sin_task_proper.erl
@@ -49,7 +49,8 @@ the project. ",
do_task(Config, State0) ->
sin_task:ensure_started(proper),
lists:foldl(
- fun(#app{name=AppName, modules=Modules}, State1) ->
+ fun(#app{name=AppName, properties=Props}, State1) ->
+ Modules = proplists:get_value(modules, Props),
sin_log:verbose(Config, "PropEr testing app ~p~n", [AppName]),
case Modules == undefined orelse length(Modules) =< 0 of
true ->
View
3 src/sin_task_release.erl
@@ -151,9 +151,6 @@ generate_rel_file(Config, State, ReleaseDir, Name, Version) ->
{save_release(Config, State, ReleaseDir, Name, Version, Release), Release}.
%% @doc Process the dependencies into a format useful for the rel depends area.
--spec process_deps([atom()],
- [AppInfo::tuple()], [AppInfo::tuple()]) ->
- [AppInfo::tuple()].
process_deps(Types, [#app{name=App, vsn=Vsn} | T], Acc) ->
case lists:keyfind(App, 1, Types) of
{App, Type} ->
View
21 src/sin_task_xref.erl
@@ -54,14 +54,12 @@ do_task(Config, State) ->
ExistingPaths = code:get_path(),
xref:start(ServerName),
- Apps = lists:map(fun(#app{name=App}) ->
- App
- end, sin_state:get_value(project_apps, State)),
+ Apps = sin_state:get_value(project_apps, State),
ModuleInfo =
- lists:flatten(lists:map(fun(App) ->
+ lists:flatten(lists:map(fun(App=#app{modules=Modules}) ->
xref_app(Config, State, ServerName, App),
- gather_modules(State, App)
+ Modules
end,
Apps)),
@@ -78,24 +76,15 @@ do_task(Config, State) ->
xref:stop(ServerName),
State.
-%% Get the module detail information from the config
--spec gather_modules(sin_state:state(), AppName::string()) ->
- [{Filename::string(), AppName::atom(), Extentions::string()}].
-gather_modules(State, AppName) ->
- sin_state:get_value({apps, AppName, module_detail}, [], State).
-
-
%% @doc add the application to the specified xref system
-spec xref_app(sin_config:config(), sin_state:state(),
ServerName::atom(), AppName::atom()) ->
ok | fail.
-xref_app(Config, State, ServerName, AppName) ->
- Paths = sin_state:get_value({apps, AppName, code_paths}, State),
+xref_app(Config, State, ServerName, #app{path=AppDir,properties=Props}) ->
+ Paths = proplists:get_value(code_paths, Props),
xref:set_library_path(ServerName, Paths),
- AppDir = sin_state:get_value({apps, AppName, builddir}, State),
-
case xref:add_application(ServerName, AppDir,
[{warnings, true}]) of
{ok, _AppNameVsn} ->
View
29 src/sin_utils.erl
@@ -20,6 +20,7 @@
copy_entire_dir/3,
delete_dir/1,
file_exists/2,
+ get_file_with_ext/2,
get_application_env/2,
get_ignore_dirs/1,
is_dir_ignorable/2,
@@ -33,7 +34,6 @@
%%====================================================================
%% API
%%====================================================================
-
%% @doc Trivially convert a value to a boolean
-spec to_bool(any()) -> boolean().
to_bool(true) ->
@@ -85,6 +85,33 @@ file_exists(State, FileName) ->
_ ->
true
end.
+%% @doc Find the first file with the specified extension
+get_file_with_ext(Dir, Ext) ->
+ case file:list_dir(Dir) of
+ {ok, Names} ->
+ case ec_lists:search(fun(Name) ->
+ %% If the name comes back exactly the
+ %% same then there was no extension
+ %% that matched. If it came back
+ %% different then it did match and we
+ %% can go from there. This is a bit
+ %% hacky but works quite well
+ case filename:basename(Name, Ext) == Name of
+ false ->
+ {ok, filename:join(Dir, Name)};
+ true ->
+ not_found
+ end
+ end, Names) of
+ {ok, Value, _} ->
+ Value;
+ not_found ->
+ false
+ end;
+ _ ->
+ false
+ end.
+
%% @doc Copies the specified directory down to the build dir on a file by file
%% basis. It only copies if the file has .
View
10 test/sint_app_src.erl
@@ -1,6 +1,7 @@
-module(sint_app_src).
-include_lib("eunit/include/eunit.hrl").
+-include_lib("sinan/include/sinan.hrl").
-export([given/3, 'when'/3, then/3]).
@@ -64,11 +65,10 @@ then([warn, the, user, that, the,
{ok, State}.
verify_ebin_app(ProjectName, BuildState) ->
- BaseDir = sin_state:get_value({apps,
- erlang:list_to_atom(ProjectName),
- builddir},
- BuildState),
- BasePath = filename:join([BaseDir, "ebin", ProjectName ++
+ App = sin_state:project_app_by_name(erlang:list_to_atom(ProjectName),
+ BuildState),
+
+ BasePath = filename:join([App#app.path, "ebin", ProjectName ++
".app"]),
?assertMatch(true,
sin_utils:file_exists(sin_state:new(), BasePath)),
View
138 test/sint_gh85_SUITE.erl
@@ -0,0 +1,138 @@
+%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
+%%%-------------------------------------------------------------------
+%%% @author Eric B Merritt <ericbmerritt@gmail.com>
+%%% @copyright (C) 2012, Erlware, LLC.
+%%%-------------------------------------------------------------------
+-module(sint_gh85_SUITE).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-export([init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ end_per_testcase/2,
+ all/1,
+ all/0,
+ normal_vsn/1,
+ v_git_vsn/1,
+ git_vsn/1]).
+
+%%====================================================================
+%% TEST SERVER CALLBACK FUNCTIONS
+%%====================================================================
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all(doc) ->
+ ["Testing the fix for github issue 85 : git based app versions "
+ "(https://github.com/erlware/sinan/issues/85)"].
+
+all() ->
+ [normal_vsn, git_vsn, v_git_vsn].
+
+%%====================================================================
+%% TEST CASES
+%%====================================================================
+
+normal_vsn(doc) ->
+ ["Create project under git with a normal version"];
+normal_vsn(suite) ->
+ [];
+normal_vsn(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ProjectName = "foo",
+ {ProjectDir, _} =
+ sint_test_project_gen:single_app_project(PrivDir, ProjectName, "1.0.0"),
+ Cmd = lists:flatten(io_lib:format("git init ~s", [ProjectDir])),
+ {ok, _} = sin_sh:sh(sin_config:new(), Cmd, []),
+ Ret = sinan:run_sinan(["-s", ProjectDir, "build"]),
+ ?assertMatch({_, _}, Ret),
+ EbinPath = filename:join([ProjectDir, "_build", ProjectName, "lib",
+ ProjectName ++ "-" ++ "1.0.0", "ebin",
+ ProjectName ++ ".app"]),
+ Result = file:consult(EbinPath),
+ ?assertMatch({ok, [{application, _, _}]}, Result),
+ {ok, [{application, _, Details}]} = Result,
+ ?assertMatch("1.0.0", proplists:get_value(vsn, Details)).
+
+git_vsn(doc) ->
+ ["Create project with a git based vsn"];
+git_vsn(suite) ->
+ [];
+git_vsn(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ProjectName = "foo_bar",
+ {ProjectDir, _} =
+ sint_test_project_gen:single_app_project(PrivDir, ProjectName),
+ ?assertMatch(ok,
+ file:write_file(filename:join([ProjectDir, "src",
+ ProjectName ++ ".app.src"]),
+ app_src(ProjectName,
+ "git"))),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git init", [{cd, ProjectDir}]),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git add *",
+ [{cd, ProjectDir}]),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git commit -a -m \"tag test\"",
+ [{cd, ProjectDir}]),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git tag 2.0.0.0", [{cd, ProjectDir}]),
+ Ret = sinan:run_sinan(["-s", ProjectDir, "build"]),
+ ?assertMatch({_, _}, Ret),
+ EbinPath = filename:join([ProjectDir, "_build", ProjectName, "lib",
+ ProjectName ++ "-" ++ "2.0.0.0", "ebin",
+ ProjectName ++ ".app"]),
+ Result = file:consult(EbinPath),
+ ?assertMatch({ok, [{application, _, _}]}, Result),
+ {ok, [{application, _, Details}]} = Result,
+ ?assertMatch("2.0.0.0", proplists:get_value(vsn, Details)).
+
+v_git_vsn(doc) ->
+ ["Create project with a git based vsn and tagged with v"];
+v_git_vsn(suite) ->
+ [];
+v_git_vsn(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ ProjectName = "foo_bar_baz",
+ {ProjectDir, _} =
+ sint_test_project_gen:single_app_project(PrivDir, ProjectName),
+ ?assertMatch(ok,
+ file:write_file(filename:join([ProjectDir, "src",
+ ProjectName ++ ".app.src"]),
+ app_src(ProjectName,
+ "{rm_v, git}"))),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git init", [{cd, ProjectDir}]),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git add *",
+ [{cd, ProjectDir}]),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git commit -a -m \"tag test\"",
+ [{cd, ProjectDir}]),
+ {ok, _} = sin_sh:sh(sin_config:new(), "git tag v2.0.0.0", [{cd, ProjectDir}]),
+ Ret = sinan:run_sinan(["-s", ProjectDir, "build"]),
+ ?assertMatch({_, _}, Ret),
+ EbinPath = filename:join([ProjectDir, "_build", ProjectName, "lib",
+ ProjectName ++ "-" ++ "2.0.0.0", "ebin",
+ ProjectName ++ ".app"]),
+ Result = file:consult(EbinPath),
+ ?assertMatch({ok, [{application, _, _}]}, Result),
+ {ok, [{application, _, Details}]} = Result,
+ ?assertMatch("2.0.0.0", proplists:get_value(vsn, Details)).
+
+ app_src(Name, Vsn) ->
+ ["%% This is the application resource file (.app file) for the app2,\n"
+ "%% application.\n"
+ "{application, ", Name, ",\n"
+ "[{description, \"Your Desc HERE\"},\n"
+ " {vsn, ", Vsn, "},\n"
+ " {modules, []},\n"
+ " {registered,[", Name, "_sup]},\n"
+ " {applications, [kernel, stdlib]},\n"
+ " {mod, {", Name, "_app,[]}}, \n"
+ " {start_phases, []}]}.\n"].
+

0 comments on commit 3fe1395

Please sign in to comment.
Something went wrong with that request. Please try again.