Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

support version control based vsn in app.src

  • Loading branch information...
commit 3fe1395c8d9c000ae1471c663ff8be50d9917458 1 parent b2fe2e1
Eric Merritt authored
24 include/sinan.hrl
@@ -14,8 +14,25 @@
14 14 path :: string(),
15 15 type :: runtime | compiletime,
16 16 project :: boolean(),
17   - modules :: sin_file_info:mod(),
18   - sources :: sin_file_info:mod()}).
  17 + %% Everything below will only be populated if the
  18 + %% project field above is true otherwise they will be
  19 + %% undefined.
  20 + id="" :: string() | list(),
  21 + maxP=infinity :: integer() | infinity,
  22 + maxT=infinity :: integer() | infinity,
  23 + env=[] :: list(),
  24 + start_phases = undefined :: term() | undefined,
  25 + basedir :: string() | undefined,
  26 + description="" :: string() | list(),
  27 + registered :: [atom()] | undefined,
  28 + applications :: [atom()] | undefined,
  29 + included_applications :: [atom()] | undefined,
  30 + dep_constraints :: term() | undefined,
  31 + mod :: term() | undefined,
  32 + dotapp :: string() | undefined,
  33 + deps :: [atom()] | undefined,
  34 + properties=[] :: proplists:proplist(),
  35 + modules :: [atom()] | undefined}).
19 36
20 37 %% @doc describes a module in the system
21 38 %% - name: is the name of the module
@@ -24,7 +41,8 @@
24 41 path :: string(),
25 42 module_deps :: [module()],
26 43 includes :: [atom()],
27   - include_timestamps :: [{string(), sin_file_info:date_time()}],
  44 + include_timestamps :: [{string(),
  45 + sin_file_info:date_time()}],
28 46 tags :: [atom()],
29 47 called_modules :: [atom()],
30 48 changed :: sin_file_info:date_time(),
202 src/sin_app_meta.erl
@@ -12,66 +12,178 @@
12 12 -include_lib("sinan/include/sinan.hrl").
13 13
14 14 %% API
15   --export([populate/2,
16   - rewrite_vsn/4,
  15 +-export([build_meta/3,
  16 + populate_modules/1,
  17 + write_app_file/1,
17 18 format_exception/1]).
18 19
  20 +%% SLASH_RE was create by doing re:compile("\\/").
  21 +-define(SLASH_RE,
  22 + {re_pattern,0,0,
  23 + <<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,
  24 + 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,
  25 + 84,0,10,0>>}).
  26 +
19 27 %%====================================================================
20 28 %% API
21 29 %%====================================================================
22   -
23   -%% @doc do the system preparation
24   --spec populate(sin_config:matcher(), sin_state:state()) -> sin_state:state().
25   -populate(Config, State0) ->
26   - ProjectApps = sin_state:get_value(project_apps, State0),
27   - lists:foldl(fun(App, State1) ->
28   - prepare_app(Config, State1, App)
29   - end, State0, ProjectApps).
30   -
31   -rewrite_vsn(Config, State, AppDir, BaseDetails) ->
32   - RWVSN = fun({vsn, VsnSpec}) ->
33   - {vsn, vcs_vsn(Config, State, VsnSpec, AppDir)};
34   - (El) -> El
35   - end,
36   - [RWVSN(Detail) || Detail <- BaseDetails].
  30 +write_app_file(#app{name=AppName,vsn=Vsn,path=Path,
  31 + description=Desc,registered=Reg,
  32 + applications=Appls,included_applications=IA,
  33 + mod=Mod,modules=AppModules,
  34 + id=Id,maxP=MaxP,maxT=MaxT,env=Env,
  35 + start_phases=StartPhases}) ->
  36 + MetaDataPath = filename:join([Path, "ebin",
  37 + erlang:atom_to_list(AppName) ++
  38 + ".app"]),
  39 + MetaData = {application, AppName,
  40 + [{description, Desc},
  41 + {id, Id},
  42 + {vsn, Vsn},
  43 + {modules, AppModules},
  44 + {maxP, MaxP},
  45 + {maxT, MaxT},
  46 + {registered, Reg},
  47 + {included_applications, IA},
  48 + {applications, Appls},
  49 + {env, Env},
  50 + {mod, Mod},
  51 + {start_phases, StartPhases}]},
  52 + file:write_file(MetaDataPath,
  53 + io_lib:format("~p.\n",
  54 + [MetaData])).
  55 +
  56 +populate_modules(App=#app{path=Path}) ->
  57 + EbinDir = filename:join(Path, "ebin"),
  58 + Modules =
  59 + filelib:fold_files(EbinDir,
  60 + ".+\.beam",
  61 + true,
  62 + fun(File, Acc) ->
  63 + PathName = filename:rootname(File, ".beam"),
  64 + Name = string:substr(PathName,
  65 + erlang:length(EbinDir) + 2),
  66 + ModuleName =
  67 + erlang:list_to_atom(erlang:binary_to_list(
  68 + erlang:iolist_to_binary(
  69 + re:replace(Name,
  70 + ?SLASH_RE,
  71 + ".",
  72 + [global])))),
  73 + [ModuleName | Acc]
  74 + end,
  75 + []),
  76 + App#app{modules=Modules}.
  77 +
  78 +build_meta(Config, State0, AppDir) ->
  79 + AppFile = get_app_file(State0, AppDir),
  80 + case file:consult(AppFile) of
  81 + {ok, [Detail={application, _, _Details}]} ->
  82 + build_out_app_description(Config, State0, AppDir, AppFile, Detail);
  83 + {error, {_, Module, Desc}} ->
  84 + Error = Module:format_error(Desc),
  85 + ?SIN_RAISE(State0, {invalid_app_file, AppFile, Error});
  86 + {error, Error} ->
  87 + ?SIN_RAISE(State0,
  88 + {no_app_file_available, AppFile, Error})
  89 + end.
37 90
38 91 %% @doc Format an exception thrown by this module
39 92 -spec format_exception(sin_exceptions:exception()) ->
40   - string().
  93 + string().
  94 +format_exception(?SIN_EXEP_UNPARSE(_, {unable_to_read_config,
  95 + AppFilePath, Error})) ->
  96 + io_lib:format("unable parse *.app file ~s:~s.",
  97 + [AppFilePath, file:format_error(Error)]);
  98 +format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, bad_name,
  99 + AppFile, AppName, WrongAppName})) ->
  100 + io_lib:format("~s file has wrong app name. Should be ~p,"
  101 + " but is ~p.", [AppFile, AppName, WrongAppName]);
  102 +format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, AppFile,
  103 + _AppName, Error})) ->
  104 + io_lib:format("Unable to parse *.app file ~s due to ~p.",
  105 + [AppFile, Error]);
  106 +format_exception(?SIN_EXEP_UNPARSE(_, {no_app_file_available,
  107 + AppFile, _AppName, Error})) ->
  108 + io_lib:format("Unable to access file ~s due to ~p.", [AppFile, Error]);
41 109 format_exception(Exception) ->
42 110 sin_exceptions:format_exception(Exception).
43 111
44 112 %%====================================================================
45 113 %% Internal functions
46 114 %%====================================================================
47   --spec prepare_app(sin_config:config(), sin_state:state(), string()) ->
48   - sin_config:config().
49   -prepare_app(_Config, State0, #app{name=AppName, path=AppBuildDir}) ->
50   - BaseDetails = populate_modules(State0, AppName),
51   - DotApp = filename:join([AppBuildDir, "ebin",
52   - erlang:atom_to_list(AppName) ++ ".app"]),
53   -
54   - ok = file:write_file(DotApp,
55   - io_lib:format("~p.\n",
56   - [{application, AppName, BaseDetails}])),
57   -
58   - State0.
59   -
60   -populate_modules(State, AppName) ->
61   - Details = sin_state:get_value({apps, AppName, base}, State),
62   -
63   - SourceModuleList = lists:map(fun({_, Module, _, _, _}) ->
64   - Module
65   - end,
66   - sin_state:get_value({apps, AppName, src_modules_detail},
67   - State)),
  115 +rewrite_vsn(Config, State, AppDir, BaseDetails) ->
  116 + RWVSN = fun({vsn, VsnSpec}) ->
  117 + {vsn, vcs_vsn(Config, State, VsnSpec, AppDir)};
  118 + (El) -> El
  119 + end,
  120 + [RWVSN(Detail) || Detail <- BaseDetails].
68 121
69   - lists:reverse(
70   - lists:foldl(fun(Element = {vsn, _}, Acc) ->
71   - [{modules, SourceModuleList}, Element | Acc];
72   - (Element, Acc) ->
73   - [Element | Acc]
74   - end, [], lists:keydelete(modules, 1, Details))).
  122 +build_out_app_description(Config, State,
  123 + AppDir, AppFile,
  124 + {application, AppName, Details0}) ->
  125 + Details1 = rewrite_vsn(Config, State, AppDir, Details0),
  126 + Applications =
  127 + case proplists:get_value(applications, Details1) of
  128 + undefined ->
  129 + [];
  130 + AppList ->
  131 + AppList
  132 + end,
  133 + IncludedApps =
  134 + case proplists:get_value(included_applications, Details1) of
  135 + undefined ->
  136 + [];
  137 + IncAppList ->
  138 + IncAppList
  139 + end,
  140 + DepConstraints =
  141 + case proplists:get_value(dep_constraints, Details1) of
  142 + undefined ->
  143 + [];
  144 + DepCons ->
  145 + DepCons
  146 + end,
  147 + Vsn = proplists:get_value(vsn, Details1),
  148 + #app{name=AppName,
  149 + vsn=Vsn,
  150 + id = proplists:get_value(id, Details1, ""),
  151 + maxP = proplists:get_value(maxP, Details1, infinity),
  152 + maxT = proplists:get_value(maxT, Details1, infinity),
  153 + env = proplists:get_value(env, Details1, []),
  154 + start_phases = proplists:get_value(start_phases,
  155 + Details1, undefined),
  156 + basedir = AppDir,
  157 + description = proplists:get_value(description, Details1),
  158 + registered =
  159 + case proplists:get_value(registered, Details1) of
  160 + undefined ->
  161 + [];
  162 + Value ->
  163 + Value
  164 + end,
  165 + applications = Applications,
  166 + included_applications = IncludedApps,
  167 + mod = proplists:get_value(mod, Details1),
  168 + dotapp=AppFile,
  169 + dep_constraints = DepConstraints,
  170 + deps=Applications ++ IncludedApps}.
  171 +
  172 +get_app_file(State, AppDir) ->
  173 + EbinDir = filename:join([AppDir, "ebin"]),
  174 + SrcDir = filename:join([AppDir, "src"]),
  175 + case sin_utils:get_file_with_ext(EbinDir, "app") of
  176 + false ->
  177 + case sin_utils:get_file_with_ext(SrcDir, "app.src") of
  178 + false ->
  179 + %% This shouldn't occur
  180 + ?SIN_RAISE(State, unable_to_find_app_file);
  181 + SrcAppFile ->
  182 + SrcAppFile
  183 + end;
  184 + AppFile ->
  185 + AppFile
  186 + end.
75 187
76 188 vcs_vsn(Config, State, Vcs, Dir) ->
77 189 case vcs_vsn_cmd(Vcs) of
361 src/sin_discover.erl
@@ -45,20 +45,19 @@
45 45 %%====================================================================
46 46
47 47 %% @doc Run the discover task.
48   --spec discover(sin_config:config(),
49   - sin_state:state()) ->
50   - sin_state:state().
51   -discover(Config0, State0) ->
52   - ProjectDir = find_project_root(State0, sin_state:get_value(start_dir, State0)),
53   - {Config1, State1}
54   - = process_raw_config(ProjectDir,
55   - find_config(ProjectDir, Config0, State0),
56   - Config0,
57   - State0),
58   - {AppDirs, State2} = look_for_app_dirs(Config1, State1,
59   - sin_state:get_value(build_dir, State1),
60   - ProjectDir),
61   - {Config1, build_app_info(State2, AppDirs, [])}.
  48 +discover(CmdLineConfig, State0) ->
  49 + ProjectDir =
  50 + find_project_root(State0, sin_state:get_value(start_dir, State0)),
  51 + {Config, State1} = create_base_config(CmdLineConfig, State0, ProjectDir),
  52 + {AppDirs, State2} =
  53 + look_for_app_dirs(Config, State1, ProjectDir),
  54 + Apps = [sin_app_meta:build_meta(Config, State2, AppDir)
  55 + || AppDir <- AppDirs],
  56 +
  57 + populate_config_and_state(ProjectDir, Config,
  58 + sin_state:store(project_applist, Apps, State2),
  59 + Apps).
  60 +
62 61
63 62 %% @doc Format an exception thrown by this module
64 63 -spec format_exception(sin_exceptions:exception()) ->
@@ -68,21 +67,6 @@ format_exception(?SIN_EXEP_UNPARSE(_, {ebin_src_conflict, Dir})) ->
68 67 "and a src/*.app.src.", [Dir]);
69 68 format_exception(?SIN_EXEP_UNPARSE(_, unable_to_intuit_config)) ->
70 69 "Unable to generate a project configuration from available metadata.";
71   -format_exception(?SIN_EXEP_UNPARSE(_, {unable_to_read_config,
72   - AppFilePath, Error})) ->
73   - io_lib:format("unable parse *.app file ~s:~s.",
74   - [AppFilePath, file:format_error(Error)]);
75   -format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, bad_name,
76   - AppFile, AppName, WrongAppName})) ->
77   - io_lib:format("~s file has wrong app name. Should be ~p,"
78   - " but is ~p.", [AppFile, AppName, WrongAppName]);
79   -format_exception(?SIN_EXEP_UNPARSE(_, {invalid_app_file, AppFile,
80   - _AppName, Error})) ->
81   - io_lib:format("Unable to parse *.app file ~s due to ~p.",
82   - [AppFile, Error]);
83   -format_exception(?SIN_EXEP_UNPARSE(_, {no_app_file_available,
84   - AppFile, _AppName, Error})) ->
85   - io_lib:format("Unable to access file ~s due to ~p.", [AppFile, Error]);
86 70 format_exception(?SIN_EXEP_UNPARSE(_, {no_app_directories,
87 71 ProjectDir})) ->
88 72 io_lib:format("Unable to find any application directories "
@@ -94,133 +78,90 @@ format_exception(Exception) ->
94 78 %%% Internal functions
95 79 %%====================================================================
96 80
  81 +create_base_config(CommandLineConfig, State0, ProjectDir) ->
  82 + HomeDir = get_home_dir(CommandLineConfig),
  83 + State1 = sin_state:store(home_dir, HomeDir, State0),
  84 + {FileConfig, State2} = get_file_config(ProjectDir, State1),
  85 + DefaultConfig =
  86 + sin_config:new_from_terms(default_config_terms(), []),
  87 + HomeConfig = get_home_config(State2, HomeDir),
  88 + {sin_config:merge(CommandLineConfig,
  89 + sin_config:merge(FileConfig,
  90 + sin_config:merge(HomeConfig,
  91 + DefaultConfig))),
  92 + State2}.
  93 +
97 94 %% @doc This trys to find the build config if it can. If the config is found it
98 95 %% is parsed in the normal way and returned to the user. however, if it is not
  96 +
99 97 %% found then the system attempts to unuit the build config from the project
100 98 %% properties.
101   --spec find_config(string(),
102   - sin_config:config(), sin_state:state()) -> sin_config:config().
103   -find_config(ProjectDir, Config, State) ->
  99 +get_file_config(ProjectDir, State) ->
104 100 ConfigPath = filename:join([ProjectDir, ?CONFIG_NAME]),
105 101 try
106   - sin_config:new_from_file(ConfigPath, [])
  102 + {sin_config:new_from_file(ConfigPath, []),
  103 + sin_state:store(intuited_config, false, State)}
107 104 catch
108 105 throw:{error_accessing_file, ConfigPath, enoent} ->
109   - intuit_build_config(ProjectDir, Config, State)
  106 + {sin_config:new(), sin_state:store(intuited_config, true, State)}
110 107 end.
111 108
112 109 %% @doc Take a raw config and merge it with the default config information
113   --spec process_raw_config(string(),
114   - sin_config:config(),
115   - sin_config:config(),
116   - sin_state:state()) ->
117   - sin_config:config().
118   -process_raw_config(ProjectDir, FileConfig, CommandLineConfig, State0) ->
119   - HomeDir = get_home_dir(CommandLineConfig),
120   - DefaultConfig =
121   - sin_config:new_from_terms(default_config_terms(), []),
122   - HomeConfig = get_home_config(State0, HomeDir),
123   - Config0 =
124   - sin_config:merge(CommandLineConfig,
125   - sin_config:merge(FileConfig,
126   - sin_config:merge(HomeConfig,
127   - DefaultConfig))),
128   - BuildDir = sin_config:match(build_dir, Config0),
  110 +populate_config_and_state(ProjectDir, Config0, State0, Apps) ->
  111 + Config1 = case sin_state:get_value(intuited_config, false, State0) of
  112 + false ->
  113 + Config0;
  114 + true ->
  115 + intuit_build_config(Config0, State0, Apps)
  116 + end,
  117 + BuildDir = sin_config:match(build_dir, Config1),
129 118 ReleaseDir = filename:join([ProjectDir,
130 119 BuildDir,
131   - get_release_name(Config0)]),
132   - State1 = sin_state:store([{release, get_release_name(Config0)},
133   - {release_vsn, sin_config:match(project_vsn, Config0)},
  120 + get_release_name(Config1)]),
  121 + State1 = sin_state:store([{release, get_release_name(Config1)},
  122 + {release_vsn, sin_config:match(project_vsn, Config1)},
134 123 {build_root, filename:join([ProjectDir,
135 124 BuildDir])},
136 125 {build_dir, ReleaseDir},
137 126 {apps_dir, filename:join(ReleaseDir, "lib")},
138 127 {release_dir, filename:join(ReleaseDir, "releases")},
139   - {home_dir, HomeDir},
140 128 {project_dir, ProjectDir}], State0),
141   -
142   -
143 129 {Config0, State1}.
144 130
145 131 %% @doc If this is a single app project then the config is generated from the
146 132 %% app name and version combined with the usual default config information. If
147 133 %% this is not a single app project then a build config cannot be intuited and
148 134 %% an exception is thrown.
149   --spec intuit_build_config(string(),
150   - sin_config:config(),
151   - sin_state:state()) ->
152   - sin_config:config().
153   -intuit_build_config(ProjectDir, Config, State) ->
154   - try
155   - AppFilePath = get_app_file(ProjectDir),
156   - case file:consult(AppFilePath) of
157   - {error, enoent} ->
158   - ?SIN_RAISE(State, unable_to_intuit_config);
159   - {error, Error} ->
160   - ?SIN_RAISE(State, {unable_to_read_config,
161   - AppFilePath, Error});
162   - {ok, [All={application, _, _}]} ->
163   - build_out_intuited_config(All)
164   - end
165   - catch
166   - throw:no_app_metadata_at_top_level ->
167   - intuit_from_command_line(Config, State)
168   - end.
169   -
170   -
171   -%% @doc the app file could be in ebin or src/app.src. we need to
172   -%% check for both
173   --spec get_app_file(string()) ->
174   - string().
175   -get_app_file(ProjectDir) ->
176   - EbinDir = filename:join(ProjectDir,
177   - "ebin"),
178   - SrcDir = filename:join(ProjectDir,
179   - "src"),
180   - EbinPath = filelib:fold_files(EbinDir, "(.+)\.app", false,
181   - fun(BaseName, Acc) ->
182   - [filename:join(EbinDir, BaseName) | Acc]
183   - end, []),
184   - SrcPath = filelib:fold_files(SrcDir, "(.+)\.app.src", false,
185   - fun(BaseName, Acc) ->
186   - [filename:join(EbinDir, BaseName) | Acc]
187   - end, []),
188   - case {EbinPath, SrcPath} of
189   - {_, [RSrcDir]} ->
190   - RSrcDir;
191   - {[REbinDir], _} ->
192   - REbinDir;
193   - _ ->
194   - throw(no_app_metadata_at_top_level)
  135 +intuit_build_config(Config0, State, Apps) ->
  136 + case intuit_from_command_line(Config0) of
  137 + undefined ->
  138 + case Apps of
  139 + [#app{name=AppName,vsn=AppVsn}] ->
  140 + sin_config:add_all([{project_name, AppName},
  141 + {project_vsn, AppVsn}], Config0);
  142 + _ ->
  143 + ?SIN_RAISE(State, unable_to_intuit_config)
  144 + end;
  145 + Config1 ->
  146 + Config1
195 147 end.
196 148
197   -%% @doc Given information from the app dir that was found, create a new full
198   -%% populated correct build config.
199   --spec build_out_intuited_config(All::{application, atom(), [{atom(), term()}]}) ->
200   - sin_config:config().
201   -build_out_intuited_config({application, AppName, Rest}) ->
202   - {value, {vsn, ProjectVsn}} = lists:keysearch(vsn, 1, Rest),
203   - sin_config:add_all([{project_name, AppName},
204   - {project_vsn, ProjectVsn}], sin_config:new()).
205   -
206 149 %% @doc The override is command line override values that are provided by the
207 150 %% user. If the user provides a project name and version we can use that
208   --spec intuit_from_command_line(sin_config:config(), sin_state:state()) ->
209   - sin_config:config().
210   -intuit_from_command_line(Config, State) ->
  151 +intuit_from_command_line(Config) ->
  152 + %% These values are already in the config since they are populated
  153 + %% automatically. We are just going to make sure they are actually there.
211 154 case sin_config:match(project_name, undefined, Config) of
212 155 undefined ->
213   - ?SIN_RAISE(State, unable_to_intuit_config);
  156 + undefined;
214 157 _PName ->
215   - ok
216   - end,
217   - case sin_config:match(project_vsn, undefined, Config) of
218   - undefined ->
219   - ?SIN_RAISE(State, unable_to_intuit_config);
220   - _PVsn ->
221   - ok
222   - end,
223   - Config.
  158 + case sin_config:match(project_vsn, undefined, Config) of
  159 + undefined ->
  160 + undefined;
  161 + _PVsn ->
  162 + Config
  163 + end
  164 + end.
224 165
225 166 %% @doc Find the root of the project. The project root may be marked by a _build
226 167 %% dir, a build config (sinan.cfg, _build.cfg). If none of those are found then
@@ -289,88 +230,15 @@ parent_dir([_H], Acc) ->
289 230 parent_dir([H | T], Acc) ->
290 231 parent_dir(T, [H | Acc]).
291 232
292   -%% @doc Given a list of app dirs retrieves the application names.
293   --spec build_app_info(sin_state:state(),
294   - List::list(),
295   - Acc::list()) ->
296   - Config::sin_config:config().
297   -build_app_info(State0, [H|T], Acc) ->
298   - {AppName, AppFile, AppDir} = get_app_info(H),
299   - case file:consult(AppFile) of
300   - {ok, [{application, AppName, Details}]} ->
301   -
302   - Applications = case lists:keyfind(applications, 1, Details) of
303   - {applications, AppList} ->
304   - AppList;
305   - false ->
306   - []
307   - end,
308   - IncludedApps = case lists:keyfind(included_applications, 1, Details) of
309   - {included_applications, IncAppList} ->
310   - IncAppList;
311   - false ->
312   - []
313   - end,
314   -
315   - State1 = source_details(AppDir, AppName,
316   - sin_state:store([{{apps, AppName, dotapp}, AppFile},
317   - {{apps, AppName, basedir}, AppDir},
318   - {{apps, AppName, deps}, Applications ++ IncludedApps}],
319   - State0)),
320   - State3 =
321   - lists:foldl(fun({Key, Value}, State4) ->
322   - sin_state:store({apps, AppName, Key}, Value, State4)
323   - end,
324   - sin_state:store({apps, AppName, base},
325   - Details, State1),
326   - Details),
327   -
328   - build_app_info(State3, T, [AppName | Acc]);
329   - {ok, [{application, WrongAppName, _Details}]} ->
330   - ?SIN_RAISE(State0,
331   - {invalid_app_file, bad_name,
332   - AppFile, AppName, WrongAppName});
333   -
334   - {error, {_, Module, Desc}} ->
335   - Error = Module:format_error(Desc),
336   - ?SIN_RAISE(State0, {invalid_app_file, AppFile, AppName, Error});
337   - {error, Error} ->
338   - ?SIN_RAISE(State0,
339   - {no_app_file_available, AppFile, AppName, Error})
340   - end;
341   -build_app_info(State, [], Acc) ->
342   - sin_state:store(project_applist, Acc, State).
343   -
344   --spec get_app_info({ebin | appsrc, string()}) -> {string(), string()}.
345   -get_app_info({ebin, Dir, AppName}) ->
346   - AppFile = filename:join([Dir, "ebin", string:concat(AppName, ".app")]),
347   - {erlang:list_to_atom(AppName), AppFile, Dir};
348   -get_app_info({appsrc, Dir, AppName}) ->
349   - AppFile = filename:join([Dir, "src", string:concat(AppName, ".app.src")]),
350   - {erlang:list_to_atom(AppName), AppFile, Dir}.
351   -
352   --spec source_details(string(), string(), sin_config:config()) ->
353   - sin_config:config().
354   -source_details(Dir, AppName, State0) ->
355   - SrcDir = filename:join([Dir, "src"]),
356   - TestDir = filename:join([Dir, "test"]),
357   -
358   - SrcModules = gather_modules(SrcDir),
359   - TestModules = gather_modules(TestDir),
360   - sin_state:store({apps, AppName, src_modules_detail}, SrcModules,
361   - sin_state:store({apps, AppName, all_modules_detail},
362   - SrcModules ++ TestModules, State0)).
363   -
364 233 %% @doc Rolls through subdirectories of the build directory looking for
365 234 %% directories that have a src and an ebin subdir. When it finds one it stops
366 235 %% recursing and adds the directory to the list to return.
367 236 -spec look_for_app_dirs(sin_config:config(),
368 237 sin_state:state(),
369   - BuildDir::string(),
370 238 ProjectDir::string()) ->
371 239 AppDirs::[string()].
372   -look_for_app_dirs(Config, State0, BuildDir, ProjectDir) ->
373   - Ignorables = sin_config:match(ignore_dirs, Config) ++ [BuildDir],
  240 +look_for_app_dirs(Config, State0, ProjectDir) ->
  241 + Ignorables = sin_config:match(ignore_dirs, Config),
374 242 State1 = sin_state:store(ignore_dirs, Ignorables, State0),
375 243 case process_possible_app_dir(State1, ProjectDir,
376 244 Ignorables, []) of
@@ -389,14 +257,11 @@ process_possible_app_dir(State, TargetDir, Ignorables, Acc) ->
389 257 case filelib:is_dir(TargetDir) andalso not
390 258 sin_utils:is_dir_ignorable(TargetDir, Ignorables) of
391 259 true ->
392   - {ok, Dirs} = file:list_dir(TargetDir),
393   - Res = has_src(State, TargetDir, Dirs),
394   - case Res of
395   - Val = {Type, TargetDir, _} when Type =:= appsrc;
396   - Type =:= ebin ->
397   - [Val | Acc];
398   -
399   - _ ->
  260 + {ok, SubDirs} = file:list_dir(TargetDir),
  261 + case is_app_directory(TargetDir, SubDirs) of
  262 + true ->
  263 + [TargetDir | Acc];
  264 + false ->
400 265 lists:foldl(fun(Sub, NAcc) ->
401 266 Dir = filename:join([TargetDir, Sub]),
402 267 process_possible_app_dir(State,
@@ -404,91 +269,39 @@ process_possible_app_dir(State, TargetDir, Ignorables, Acc) ->
404 269
405 270 Ignorables,
406 271 NAcc)
407   - end, Acc, Dirs)
  272 + end, Acc, SubDirs)
408 273 end;
409 274 false ->
410 275 Acc
411 276 end.
412 277
413   --spec has_src(sin_state:state(),
414   - string(), [string()]) ->
  278 +is_app_directory(BaseDir, SubDirs) ->
  279 + has_ebin(BaseDir, SubDirs) orelse has_src(BaseDir, SubDirs).
  280 +
  281 +-spec has_src(string(), [string()]) ->
415 282 false | {appsrc | ebin, string()}.
416   -has_src(State, BaseDir, SubDirs) ->
  283 +has_src(BaseDir, SubDirs) ->
417 284 case lists:member("src", SubDirs) of
418 285 true ->
419   - case has_file(BaseDir, "src", ".app.src") of
420   - false ->
421   - has_ebin(State, BaseDir, SubDirs);
422   - AppName ->
423   - {appsrc, BaseDir, AppName}
424   - end;
  286 + sin_utils:get_file_with_ext(
  287 + filename:join(BaseDir, "src"), ".app.src") =/= false;
425 288 false ->
426   - has_ebin(State, BaseDir, SubDirs)
  289 + false
427 290 end.
428 291
429   --spec has_ebin(sin_state:state(),
430   - string(), [string()]) ->
  292 +-spec has_ebin(string(), [string()]) ->
431 293 false | {appsrc | ebin, string()}.
432   -has_ebin(_State, BaseDir, SubDirs) ->
  294 +has_ebin(BaseDir, SubDirs) ->
433 295 case lists:member("ebin", SubDirs) of
434 296 true ->
435   - case has_file(BaseDir, "ebin", ".app") of
436   - false ->
437   - false;
438   - AppName ->
439   - {ebin, BaseDir, AppName}
440   - end;
  297 + sin_utils:get_file_with_ext(
  298 + filename:join(BaseDir, "ebin"), ".app") =/= false;
441 299 false ->
442 300 false
443 301 end.
444 302
445   -has_file(BaseDir, SrcDir, Ext) ->
446   - case file:list_dir(filename:join(BaseDir, SrcDir)) of
447   - {ok, Names} ->
448   - lists:foldl(fun(Name, false) ->
449   - case filename:basename(Name, Ext) of
450   - Name ->
451   - false;
452   - BaseName ->
453   - BaseName
454   - end;
455   - (_, Acc) ->
456   - Acc
457   - end, false, Names);
458   - _ ->
459   - false
460   - end.
461   -
462 303
463 304
464   -%% @doc Gather the list of modules that currently may need to be built.
465   -gather_modules(SrcDir) ->
466   - case filelib:is_dir(SrcDir) of
467   - true ->
468   - filelib:fold_files(SrcDir,
469   - "(.+\.erl|.+\.yrl|.+\.asn1|.+\.asn)$",
470   - true, % Recurse into subdirectories of src
471   - fun(File, Acc) ->
472   - Ext = filename:extension(File),
473   - AtomExt = list_to_atom(Ext),
474   - TestImplementations =
475   - case string:str(File, "_SUITE.erl") of
476   - 0 ->
477   - [];
478   - _ ->
479   - [common_test]
480   - end,
481   - [{File, module_name(File), Ext, AtomExt,
482   - TestImplementations} | Acc]
483   - end, []);
484   - false ->
485   - []
486   - end.
487   -
488   -%% @doc Extract the module name from the file name.
489   -module_name(File) ->
490   - list_to_atom(filename:rootname(filename:basename(File))).
491   -
492 305 %% @doc the minimal default configuration info
493 306 default_config_terms() ->
494 307 [{build_dir, "_build"},
@@ -528,7 +341,3 @@ get_home_config(State, HomeDir) ->
528 341 false ->
529 342 sin_config:new()
530 343 end.
531   -
532   -%%====================================================================
533   -%% tests
534   -%%====================================================================
55 src/sin_fs_resolver.erl
@@ -44,12 +44,19 @@ new(Config, State) ->
44 44 {sin_dep_resolver:state(),
45 45 [sin_dep_solver:spec()]}.
46 46 app_dependencies(RState={PathList, State, ProjectApps}, App, Ver) ->
47   - case lists:member(App, ProjectApps) of
48   - true ->
49   - {Deps, VersionedDeps} = get_app_constraints(State,
50   - sin_state:get_value({apps, App, dotapp}, State)),
51   - {RState, Deps ++ VersionedDeps};
52   - false ->
  47 + case ec_lists:search(fun(#app{name=AppName,vsn=Vsn,
  48 + deps=Deps, dep_constraints=DepC}) ->
  49 + case App == AppName
  50 + andalso Vsn == Ver of
  51 + true ->
  52 + {ok, Deps ++ DepC};
  53 + _ ->
  54 + not_found
  55 + end
  56 + end, ProjectApps) of
  57 + {ok, Deps, _} ->
  58 + {RState, Deps};
  59 + not_found ->
53 60 case look_for_dependency_path(State, PathList, App, Ver) of
54 61 {ok, Path} ->
55 62 {RState, get_dependency_information(State, Path, App)};
@@ -62,10 +69,17 @@ app_dependencies(RState={PathList, State, ProjectApps}, App, Ver) ->
62 69 {sin_dep_resolver:impl(),
63 70 [sin_dep_solver:versions()]}.
64 71 app_versions(RState={PathList, State, ProjectApps}, App) ->
65   - case lists:member(App, ProjectApps) of
66   - true ->
67   - {RState, [get_app_vsn(State, sin_state:get_value({apps, App, dotapp}, State))]};
68   - false ->
  72 + case ec_lists:search(fun(#app{name=AppName,vsn=Vsn}) ->
  73 + case App == AppName of
  74 + true ->
  75 + {ok, Vsn};
  76 + _ ->
  77 + not_found
  78 + end
  79 + end, ProjectApps) of
  80 + {ok, Vsn, _} ->
  81 + {RState, [Vsn]};
  82 + not_found ->
69 83 VsnList = get_available_versions(State, PathList, App),
70 84 %% Make sure the elements are unique
71 85 {RState, sets:to_list(sets:from_list(VsnList))}
@@ -75,7 +89,9 @@ app_versions(RState={PathList, State, ProjectApps}, App) ->
75 89 -spec resolve(sin_dep_resolver:impl(), sin_dep_solver:app(), sin_dep_solver:version()) ->
76 90 {sin_dep_resolver:impl(), string()}.
77 91 resolve(RState={PathList, State, ProjectApps}, App, Version) ->
78   - case lists:member(App, ProjectApps) of
  92 + case lists:any(fun(#app{name=AppName}) ->
  93 + AppName == App
  94 + end, ProjectApps) of
79 95 true ->
80 96 AppsBuildDir = sin_state:get_value(apps_dir, State),
81 97 Path =
@@ -224,20 +240,3 @@ get_available_versions(State, PathList, App) ->
224 240 {unable_to_access_directory, Error, Path})
225 241 end
226 242 end, [], PathList).
227   -
228   --spec get_app_vsn(sin_state:state(), [string()]) ->
229   - string().
230   -get_app_vsn(State, AppConfigPath) ->
231   - case file:consult(AppConfigPath) of
232   - {ok, [{application, _AppName, Terms}]} ->
233   - case lists:keyfind(vsn, 1, Terms) of
234   - {vsn, Vsn} ->
235   - Vsn;
236   - _ ->
237   - ?SIN_RAISE(State, {app_config_contains_no_version, AppConfigPath})
238   - end;
239   - {error, enoent} ->
240   - ?SIN_RAISE(State, {no_app_config, AppConfigPath});
241   - {error, SomeOtherError} ->
242   - ?SIN_RAISE(State, {error_opening_file, AppConfigPath, SomeOtherError})
243   - end.
17 src/sin_state.erl
@@ -16,6 +16,7 @@
16 16 get_value/2,
17 17 get_value/3,
18 18 get_pairs/1,
  19 + project_app_by_name/2,
19 20 delete/2,
20 21 add_run_error/3,
21 22 get_run_errors/1,
@@ -88,6 +89,22 @@ get_value(Key, DefaultValue, State) ->
88 89 delete(Key, State) ->
89 90 dict:erase(Key, State).
90 91
  92 +project_app_by_name(AppName, State) ->
  93 + ProjectApps = sin_state:get_value(project_apps, State),
  94 + case ec_lists:search(fun(App=#app{name=Name}) ->
  95 + case Name == AppName of
  96 + true ->
  97 + {ok, App};
  98 + _ ->
  99 + not_found
  100 + end
  101 + end, ProjectApps) of
  102 + {ok, ProjApp, _} ->
  103 + ProjApp;
  104 + _ ->
  105 + not_found
  106 + end.
  107 +
91 108 %% @doc Get the complete state as key,value pairs
92 109 -spec get_pairs(state()) -> [{key(), value()}].
93 110 get_pairs(State) ->
48 src/sin_task_build.erl
@@ -93,8 +93,13 @@ do_task(Config, State0) ->
93 93 Apps0 = sin_state:get_value(project_apps, State0),
94 94 NApps = reorder_apps_according_to_deps(State0, Apps0),
95 95 {State1, Apps1} = build_apps(Config, State0, NApps),
96   - sin_app_meta:populate(Config, sin_state:store(project_apps, Apps1, State1)).
97   -
  96 + Apps2 = [sin_app_meta:populate_modules(App) || App <- Apps1],
  97 + State2 =
  98 + sin_state:store(project_apps, Apps2, State1),
  99 + lists:foreach(fun(App) ->
  100 + sin_app_meta:write_app_file(App)
  101 + end, Apps2),
  102 + State2.
98 103
99 104 %% @doc Gather up all the errors and warnings for output.
100 105 -spec gather_fail_info([term()], string()) ->
@@ -151,9 +156,7 @@ format_exception(Exception) ->
151 156 [sinan:app()]) -> list().
152 157 reorder_apps_according_to_deps(State, AllApps) ->
153 158 ReOrdered = lists:foldr(
154   - fun (#app{name=AppName}, Acc) ->
155   - AllDeps = sin_state:get_value({apps, AppName, deps},
156   - State),
  159 + fun (#app{name=AppName, deps=AllDeps}, Acc) ->
157 160 case map_deps(AppName, AllDeps, AllApps) of
158 161 [] ->
159 162 [{'NONE', AppName} | Acc];
@@ -218,24 +221,30 @@ build_apps(Config, State0, Apps0) ->
218 221 Apps3 = lists:reverse(Apps2),
219 222 {State2, Apps3}.
220 223
221   -prepare_app(Config, State0, App0 = #app{name=AppName, path=Path}, BuildDir, Ignorables) ->
222   - AppDir = sin_state:get_value({apps, AppName, basedir}, State0),
  224 +prepare_app(Config, State0,
  225 + App0 = #app{path=Path,basedir=AppDir},
  226 + BuildDir, Ignorables) ->
223 227
224 228 %% Ignore the build dir when copying or we will create a deep monster in a
225 229 %% few builds
226 230 State1 = sin_utils:copy_dir(Config, State0, Path, AppDir, "",
227 231 [BuildDir | Ignorables], []),
228 232
229   - State2 = sin_state:store({apps, AppName, builddir},
230   - Path, State1),
  233 +
  234 +
  235 +
  236 +
  237 +
  238 +
  239 +
231 240
232 241 Ebin = filename:join(Path, "ebin"),
233 242 ec_file:mkdir_path(Ebin),
234 243 true = code:add_patha(Ebin),
235 244
236   - {State3, Mods} =
237   - process_source_files(State2, AppDir),
238   - {State3, App0#app{sources=Mods}}.
  245 + {State2, Mods} =
  246 + process_source_files(State1, AppDir),
  247 + {State2, App0#app{properties=[{sources,Mods} | App0#app.properties]}}.
239 248
240 249 -spec process_source_files(sin_state:state(), atom()) ->
241 250 {sin_state:state(), [sinan:mod()]}.
@@ -244,7 +253,6 @@ process_source_files(State0, AppDir) ->
244 253 TestDir = filename:join(AppDir, "test"),
245 254 IncludeDir = filename:join(AppDir, "include"),
246 255 Includes = [SrcDir, TestDir, IncludeDir],
247   -
248 256 {State1, SrcModules} = process_source_files_in_path(State0, SrcDir, Includes),
249 257 {State2, TestModules} = process_source_files_in_path(State1, TestDir, Includes),
250 258 {State2, SrcModules ++ TestModules}.
@@ -263,22 +271,24 @@ process_source_files_in_path(State0, Dir, Includes) ->
263 271 %% @doc Build an individual otp application.
264 272 build_app(Config0, State0, App=#app{name=AppName,
265 273 path=AppBuildDir,
266   - sources=Modules0}) ->
  274 + basedir=AppDir,
  275 + properties=Properties}) ->
  276 + Sources = proplists:get_value(sources, Properties),
267 277 Config1 = Config0:specialize([{app, AppName}]),
268   - AppDir = sin_state:get_value({apps, AppName, basedir}, State0),
269 278
270 279 Target = filename:join([AppBuildDir, "ebin"]),
271 280
272   - {State3, NewModules} =
  281 + {State3, Modules} =
273 282 lists:foldl(fun(Module, {State1, Modules1}) ->
274 283 {State2, Mods} =
275 284 build_source_if_required(Config1, State1,
276 285 Module,
277 286 AppDir,
278   - Target, Modules0),
  287 + Target, Sources),
279 288 {State2, [Mods | Modules1]}
280   - end, {State0, []}, Modules0),
281   - {State3, App#app{modules=lists:flatten(NewModules)}}.
  289 + end, {State0, []}, Sources),
  290 + {State3, App#app{properties=[{modules, lists:flatten(Modules)} |
  291 + Properties]}}.
282 292
283 293
284 294 %% @doc go through each source file building with the correct build module.
10 src/sin_task_cucumber.erl
@@ -73,7 +73,9 @@ do_task(Config, State) ->
73 73 case AdditionalArgs of
74 74 ["gen", Name, "where", TargetApp] ->
75 75 [ {F,
76   - gen_feature(State, F, TargetApp)}
  76 + gen_feature(F,
  77 + sin_utils:find_app_by_name(erlang:list_to_atom(TargetApp),
  78 + State))}
77 79 || F <- Features, filename:basename(F, ".feature") == Name];
78 80 [] ->
79 81 [ {F, run_feature(Config, State, F)} || F <- Features ];
@@ -119,11 +121,9 @@ run_feature(Config, State, FeatureFile) ->
119 121 ?SIN_RAISE(State, {no_implementation, FeatureFile})
120 122 end.
121 123
122   -gen_feature(State, FeatureFile, TargetApp) ->
  124 +gen_feature(FeatureFile, #app{basedir=BaseDir}) ->
123 125 TargetDir =
124   - filename:join(
125   - [sin_state:get_value({apps, erlang:list_to_atom(TargetApp), basedir}, State),
126   - "test"]),
  126 + filename:join(BaseDir, "test"),
127 127 ok = ec_file:mkdir_path(TargetDir),
128 128 ok = cucumberl_gen:gen(FeatureFile, TargetDir).
129 129
90 src/sin_task_depends.erl
@@ -121,7 +121,6 @@ do_task(Config, State0) ->
121 121 true = code:add_patha(Ebin)
122 122 end, CompiletimeDeps),
123 123
124   -
125 124 MergedDeps = RuntimeDeps0 ++ CompiletimeDeps,
126 125
127 126 FA = fun (App) ->
@@ -148,8 +147,7 @@ do_task(Config, State0) ->
148 147 solve_deps(Config, State0, ProjectApps) ->
149 148 DefaultConstraints = Config:match(dep_constraints, []),
150 149
151   - {Apps, ActualSpecs} = remove_excluded(State0,
152   - ProjectApps,
  150 + {Apps, ActualSpecs} = remove_excluded(ProjectApps,
153 151 DefaultConstraints),
154 152
155 153 ResolverState0 = sin_dep_resolver:new(sin_fs_resolver, Config, State0),
@@ -162,34 +160,18 @@ solve_deps(Config, State0, ProjectApps) ->
162 160
163 161 ResolverState1 = sin_dep_solver:extract_resolver_state(SolverState2),
164 162 {ReleaseApps1, RuntimeDeps2} =
165   - lists:foldl(fun({App, Vsn}, {ReleaseApps0, RuntimeDeps1}) ->
166   - {_, Path} =
167   - sin_dep_resolver:resolve(ResolverState1,
168   - App, Vsn),
169   - case lists:member(App, ProjectApps) of
170   - true ->
171   - {[#app{name=App, vsn=Vsn, path=Path,
172   - type=runtime,
173   - project=true} | ReleaseApps0],
174   - RuntimeDeps1};
175   - false ->
176   - {ReleaseApps0,
177   - [#app{name=App, vsn=Vsn, path=Path,
178   - type=runtime,
179   - project=false} | RuntimeDeps1]}
180   - end
181   - end, {[], []}, RuntimeDeps0),
  163 + seperate_resolve_deps(ResolverState1, RuntimeDeps0, ProjectApps),
182 164
183 165 CompiletimeDeps1 =
184   - lists:foldl(fun({App, Vsn}, Acc) ->
185   - case in_runtime(App, RuntimeDeps2) of
  166 + lists:foldl(fun({AppName, Vsn}, Acc) ->
  167 + case in_runtime(AppName, RuntimeDeps2) of
186 168 true ->
187 169 Acc;
188 170 false ->
189 171 {_, Path} =
190 172 sin_dep_resolver:resolve(ResolverState1,
191   - App, Vsn),
192   - [#app{name=App, vsn=Vsn, path=Path,
  173 + AppName, Vsn),
  174 + [#app{name=AppName, vsn=Vsn, path=Path,
193 175 type=compiletime, project=false} |
194 176 Acc]
195 177 end
@@ -203,6 +185,36 @@ solve_deps(Config, State0, ProjectApps) ->
203 185
204 186 {State1, {ReleaseApps1, RuntimeDeps2, CompiletimeDeps1}}.
205 187
  188 +seperate_resolve_deps(ResolverState1, RuntimeDeps0, ProjectApps) ->
  189 + lists:foldl(fun({AppName, Vsn}, {ReleaseApps0, RuntimeDeps1}) ->
  190 + {_, Path} =
  191 + sin_dep_resolver:resolve(ResolverState1,
  192 + AppName, Vsn),
  193 + case get_project_app(AppName, ProjectApps) of
  194 + {ok, PApp, _} ->
  195 + {[PApp#app{path=Path,
  196 + type=runtime,
  197 + project=true} | ReleaseApps0],
  198 + RuntimeDeps1};
  199 + not_found ->
  200 + {ReleaseApps0,
  201 + [#app{name=AppName, vsn=Vsn, path=Path,
  202 + type=runtime,
  203 + project=false} | RuntimeDeps1]}
  204 + end
  205 + end, {[], []}, RuntimeDeps0).
  206 +
  207 +get_project_app(AppName, ProjectApps) ->
  208 + ec_lists:search(fun(PApp=#app{name=PAppName}) ->
  209 + case PAppName == AppName of
  210 + true ->
  211 + {ok,PApp};
  212 + _ ->
  213 + not_found
  214 + end
  215 + end,
  216 + ProjectApps).
  217 +
206 218 in_runtime(App0, RuntimeDeps) ->
207 219 lists:any(fun(#app{name=AppName})
208 220 when App0 == AppName ->
@@ -242,12 +254,12 @@ format_exception(Exception) ->
242 254 sin_state:state(),
243 255 sin_dep_solver:state(),
244 256 [sin_dep_solver:spec()]) ->
245   - {sin_dep_solver:state(), [sin_dep_solver:spec()]}.
  257 + {sin_dep_solver:state(),
  258 + [sin_dep_solver:spec()]}.
246 259 get_compiletime_deps(Config, State0, SolverState0, DefaultSpecs) ->
247 260 CompiletimeApps0 = Config:match(compile_deps, []),
248 261
249   - {CompiletimeApps1, CompileSpecs} = remove_excluded(State0,
250   - CompiletimeApps0,
  262 + {CompiletimeApps1, CompileSpecs} = remove_excluded(CompiletimeApps0,
251 263 DefaultSpecs),
252 264 CompiletimeDeps0 =
253 265 case sin_dep_solver:all_deps(Config, SolverState0,
@@ -279,22 +291,12 @@ get_runtime_deps(Config, State0, SolverState0, Apps, Specs) ->
279 291 end, Deps0)}
280 292 end.
281 293
282   --spec remove_excluded(sin_state:state(),
283   - [sin_dep_solver:app()],
284   - [sin_dep_solver:spec()]) ->
285   - {[sin_dep_solver:app()], [sin_dep_solver:spec()]}.
286   -remove_excluded(State0, Apps0, Constraints) ->
287   - Apps1 = lists:filter(fun(App) ->
288   - not lists:member({exclude, App}, Constraints)
  294 +remove_excluded(Apps0, Constraints) ->
  295 + Apps1 = lists:filter(fun(#app{name=AppName}) ->
  296 + not lists:member({exclude, AppName},
  297 + Constraints)
289 298 end, Apps0),
290 299 DefaultSpecs =
291   - lists:map(fun(AppName) ->
292   - case sin_state:get_value({apps, AppName, vsn},
293   - State0) of
294   - undefined ->
295   - AppName;
296   - Vsn ->
297   - {AppName, Vsn}
298   - end
299   - end, Apps1),
300   - {Apps1, DefaultSpecs ++ Constraints}.
  300 + [{AppName, Vsn} || #app{name=AppName, vsn=Vsn} <- Apps1],
  301 +
  302 + {[AppName || #app{name=AppName} <- Apps1], DefaultSpecs ++ Constraints}.
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
36 36 finds. Note that you *must* have a licensed version of Quick Check installed
37 37 on the box for this to work.",
38 38
39   -
40   -
41 39 #task{name = ?TASK,
42 40 task_impl = ?MODULE,
43 41 bare = false,
@@ -50,7 +48,8 @@ finds. Note that you *must* have a licensed version of Quick Check installed
50 48 %% @doc run all tests for all modules in the system
51 49 do_task(Config, State0) ->
52 50 lists:foldl(
53   - fun(#app{name=AppName, modules=Modules}, State1) ->
  51 + fun(#app{name=AppName, properties=Props}, State1) ->
  52 + Modules = proplists:get_value(modules, Props),
54 53 io:format("Quick Check testing app ~p~n", [AppName]),
55 54 case Modules == undefined orelse length(Modules) =< 0 of
56 55 true ->
36 src/sin_task_eunit.erl
@@ -70,7 +70,7 @@ filter_modules(Config, Modules) ->
70 70 Modules;
71 71 FilterModules ->
72 72 [Mod || Mod <- Modules,
73   - lists:member(atom_to_list(Mod#module.name),
  73 + lists:member(atom_to_list(Mod),
74 74 FilterModules)]
75 75 end.
76 76
@@ -78,24 +78,18 @@ filter_modules(Config, Modules) ->
78 78 -spec run_module_tests(sin_config:config(),
79 79 sin_state:state(), [sin_file_info:mod()]) -> ok.
80 80 run_module_tests(Config, State0, AllModules) ->
81   - lists:foldl(
82   - fun(#module{name=PreName}, State1) ->
83   - Name = case PreName of
84   - {Name0, _} ->
85   - Name0;
86   - Name0 ->
87   - Name0
88   - end,
89   - case lists:member({test, 0}, Name:module_info(exports)) of
90   - true ->
91   - sin_log:normal(Config, "testing ~p", [Name]),
92   - case eunit:test(Name, [{verbose, Config:match(verbose, false)}]) of
93   - error ->
94   - sin_state:add_run_error(Name, eunit_failure, State1);
95   - _ ->
96   - State1
97   - end;
98   - _ ->
99   - State1
100   - end
  81 + lists:foldl(fun(Name, State1) ->
  82 + case lists:member({test, 0}, Name:module_info(exports)) of
  83 + true ->
  84 + sin_log:normal(Config, "testing ~p", [Name]),
  85 + case eunit:test(Name,
  86 + [{verbose, Config:match(verbose, false)}]) of