---
 ebin/sinan.app               |    1 +
 features/sin_app_src.feature |   28 +++++++
 src/sin_discover.erl         |  185 ++++++++++++++++++++++++------------------
 src/sin_task.erl             |    1 +
 src/sin_task_build.erl       |   30 +++-----
 src/sin_task_prepare.erl     |   90 ++++++++++++++++++++
 test/sin_app_src.erl         |  120 +++++++++++++++++++++++++++
 7 files changed, 355 insertions(+), 100 deletions(-)
 create mode 100644 features/sin_app_src.feature
 create mode 100644 src/sin_task_prepare.erl
 create mode 100644 test/sin_app_src.erl

diff --git a/ebin/sinan.app b/ebin/sinan.app
index cdfea50..31dbdad 100644
--- a/ebin/sinan.app
+++ b/ebin/sinan.app
@@ -32,6 +32,7 @@
              sin_task_test,
              sin_task_xref,
              sin_task_erts,
+             sin_task_prepare,
              sin_compile_yrl,
              sin_compile_erl,
              sin_exceptions,
diff --git a/features/sin_app_src.feature b/features/sin_app_src.feature
new file mode 100644
index 0000000..a007062
--- /dev/null
+++ b/features/sin_app_src.feature
@@ -0,0 +1,28 @@
+Feature: Support app.src files in the src directory
+  In order to more obvious what are actually source files (any kind of source 
file)
+  As an Erlang Developer
+  I want to be able to put an <app-name>.app.src file in my src directory
+  and have it be copied into the correct place
+
+  Scenario: Have an app.src handled correctly when no ebin exists
+    Given a generated project that contains an app.src
+    And does not contain an ebin/app
+    When a build step is run on this project
+    Then sinan should put the app file in ebin/.app
+    And build the app normally
+
+  Scenario: Have an ebin/app handled correctly when no app.src exists
+    Given a generated project that contains an ebin/app
+    And does not contain an app.src
+    When a build step is run on this project
+    Then sinan should put the app file in ebin/.app
+    And build the app normally
+
+  Scenario: Have a build errors when an ebin/app and src/app.src exists
+    Given a generated project that contains an app.src
+    And contains an ebin/app
+    When a build step is run on this project
+    Then sinan should warn the user that both an ebin/app and app.src exists
+    And the build should fail
+
+
diff --git a/src/sin_discover.erl b/src/sin_discover.erl
index d7ac1eb..31e12b5 100644
--- a/src/sin_discover.erl
+++ b/src/sin_discover.erl
@@ -115,16 +115,39 @@ process_raw_config(ProjectDir, Config, Override) ->
 intuit_build_config(ProjectDir, Override) ->
     %% App name is always the same as the name of the project dir
     AppName = filename:basename(ProjectDir),
-    case file:consult(filename:join([ProjectDir,
-                                     "ebin",
-                                     AppName ++ ".app"])) of
-        {error, enoent} ->
-            intuit_from_override(Override);
-        {error, _} ->
-            ?SIN_RAISE(Override, unable_to_intuit_config,
-                       "Unable to generate a project config");
-        {ok, [{application, _, Rest}]} ->
-            build_out_intuited_config(AppName, Rest)
+    try
+        case file:consult(get_app_file(ProjectDir, AppName, Override)) of
+            {error, _} ->
+                ?SIN_RAISE(Override, unable_to_intuit_config,
+                           "Unable to generate a project config");
+            {ok, [{application, _, Rest}]} ->
+                build_out_intuited_config(AppName, Rest)
+        end
+    catch
+        throw:no_app_metadata_at_top_level ->
+            intuit_from_override(Override)
+    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(), sin_config:config()) ->
+                          string().
+get_app_file(ProjectDir, AppName, Config) ->
+    EbinDir = filename:join([ProjectDir,
+                             "ebin",
+                             AppName ++ ".app"]),
+    SrcDir = filename:join([ProjectDir,
+                            "src",
+                             AppName ++ ".app.src"]),
+    case {sin_utils:file_exists(Config, EbinDir),
+          sin_utils:file_exists(Config, SrcDir)} of
+        {_, true} ->
+            SrcDir;
+        {true, _} ->
+            EbinDir;
+         _ ->
+            throw(no_app_metadata_at_top_level)
     end.
 
 %% @doc Given information from the app dir that was found, create a new full
@@ -247,17 +270,18 @@ parent_dir([H | T], Acc) ->
                      Acc::list()) ->
     Config::sin_config:config().
 build_app_info(Config, [H|T], Acc) ->
-    AppName = filename:basename(H),
-    AppFile = filename:join([H, "ebin", string:concat(AppName, ".app")]),
+    {AppName, AppFile, AppDir} = get_app_info(H),
     case file:consult(AppFile) of
         {ok, [{application, Name, Details}]} ->
-            Config2 = source_details(H, AppName,
+            Config2 = source_details(AppDir, AppName,
                         process_details("apps." ++ AppName ++ ".",
                                         [{"name", Name},
                                          {"dotapp", AppFile},
-                                         {"basedir", H} | Details], Config))
-                ,
-            build_app_info(Config2,  T, [AppName | Acc]);
+                                         {"basedir", AppDir} | Details],
+                                        Config)),
+            Config3 = sin_config:store(Config2, "apps." ++ AppName ++ ".base",
+                                       Details),
+            build_app_info(Config3,  T, [AppName | Acc]);
         {error, {_, Module, Desc}} ->
             Error = Module:format_error(Desc),
             ?SIN_RAISE(Config, {invalid_app_file, Error},
@@ -271,6 +295,16 @@ build_app_info(Config, [H|T], Acc) ->
 build_app_info(Config, [], Acc) ->
     sin_config:store(Config, "project.applist", Acc).
 
+-spec get_app_info({ebin | appsrc, string()}) -> {string(), string()}.
+get_app_info({ebin, Dir}) ->
+    AppName = filename:basename(Dir),
+    AppFile = filename:join([Dir, "ebin", string:concat(AppName, ".app")]),
+    {AppName, AppFile, Dir};
+get_app_info({appsrc, Dir}) ->
+    AppName = filename:basename(Dir),
+    AppFile = filename:join([Dir, "src", string:concat(AppName, ".app.src")]),
+    {AppName, AppFile, Dir}.
+
 -spec source_details(string(), string(), sin_config:config()) ->
     sin_config:config().
 source_details(Dir, AppName, Config) ->
@@ -309,8 +343,8 @@ look_for_app_dirs(Config, BuildDir, ProjectDir) ->
                                    {ok, Value} -> Value;
                                    _ -> []
                                end,
-    case look_for_app_dirs(Config, BuildDir, ProjectDir, "",
-                           Ignorables, []) of
+    case process_possible_app_dir(Config, BuildDir, ProjectDir,
+                                  Ignorables, []) of
         [] ->
             ?SIN_RAISE(Config, no_app_directories,
                        "Unable to find any application directories."
@@ -319,76 +353,67 @@ look_for_app_dirs(Config, BuildDir, ProjectDir) ->
             Else
     end.
 
-look_for_app_dirs(_, BuildDir, _Parent, BuildDir, _Ignore, Acc) ->
-    Acc;
-look_for_app_dirs(Config, BuildDir, Parent, Sub, Ignorables, Acc) ->
-    case sin_utils:is_dir_ignorable(Sub, Ignorables) or
-        sin_utils:is_dir_ignorable(filename:join([Parent, Sub]), Ignorables) of
-        true ->
-            Acc;
-        false ->
-            process_app_dir(Config, BuildDir, Parent, Sub, Ignorables, Acc)
-    end.
-
 %% @doc Process the app dir to see if it is an application directory.
--spec process_app_dir(Config::sin_config:config(),
-                      BuildDir::string(), Parent::string(), Sub::string(),
-                      Ignorables::[string()], Acc::list()) ->
-    ListOfDirs::[string()].
-process_app_dir(Config, BuildDir, Parent, Sub, Ignorables, Acc) ->
-    Pwd = filename:join([Parent, Sub]),
-    case filelib:is_dir(Pwd) of
+-spec process_possible_app_dir(Config::sin_config:config(),
+                               BuildDir::string(),
+                               TargetDir::string(),
+                               Ignorables::[string()], Acc::list()) ->
+                                      ListOfDirs::[{ebin | appsrc, string()}].
+process_possible_app_dir(Config, BuildDir, TargetDir, Ignorables, Acc) ->
+    PossibleAppName = filename:basename(TargetDir),
+    case filelib:is_dir(TargetDir) andalso not
+        sin_utils:is_dir_ignorable(TargetDir, Ignorables) of
         true ->
-            {ok, Dirs} = file:list_dir(Pwd),
-            Res = lists:foldl(fun(F, AccIn) ->
-                                      File = filename:join([Pwd, F]),
-                                      process_dirs(File, F, AccIn)
-                              end, none, Dirs),
-            case {Res, Dirs} of
-                {both, _} ->
-                    [Pwd | Acc];
-                {_, []} ->
-                    Acc;
-                {_, _} ->
-                    lists:foldl(fun(Elem, NAcc) ->
-                                        look_for_app_dirs(Config,
-                                                          BuildDir,
-                                                          Pwd, Elem,
-                                                          Ignorables,
-                                                          NAcc)
+            {ok, Dirs} = file:list_dir(TargetDir),
+            Ebin = has_src_ebin_dotapp(Config, PossibleAppName,
+                                       TargetDir, Dirs),
+            AppSrc =  has_src_appsrc(Config, PossibleAppName,
+                                     TargetDir, Dirs),
+            case {AppSrc, Ebin} of
+                {true, true} ->
+                    ?SIN_RAISE(Config,
+                               "conflict: ~s has both an ebin/*.app "
+                               "and a src/*.app.src ", [TargetDir]);
+                {true, _} ->
+                    [{appsrc, TargetDir} | Acc];
+                {_, true}  ->
+                    [{ebin, TargetDir} | Acc];
+                _ ->
+                    lists:foldl(fun(Sub, NAcc) ->
+                                        Dir = filename:join([TargetDir, Sub]),
+                                        process_possible_app_dir(Config,
+                                                                 BuildDir,
+                                                                 Dir,
+
+                                                                 Ignorables,
+                                                                 NAcc)
                                 end, Acc, Dirs)
             end;
         false ->
             Acc
     end.
 
-%% @doc Given a directory checks of the name is src or ebin, compares against
-%%  its state and returns an indicator if the parent is a app dir.
--spec process_dirs(File::string(), FinateState::string(), src | ebin) ->
-    src | ebin.
-process_dirs(File, F, ebin) ->
-    case {filelib:is_dir(File), F} of
-        {true, "src"} ->
-            both;
-        _ ->
-            ebin
-    end;
-process_dirs(File, F, src) ->
-    case {filelib:is_dir(File), F} of
-        {true, "ebin"} ->
-            both;
-        _ ->
-            src
-    end;
-process_dirs(File, F, Type)  ->
-    case {filelib:is_dir(File), F} of
-        {true, "ebin"} ->
-            ebin;
-        {true, "src"} ->
-            src;
-        _ ->
-            Type
-    end.
+-spec has_src_ebin_dotapp(sin_config:config(),
+                          string(), string(), [string()]) ->
+                                 boolean().
+has_src_ebin_dotapp(Config, BaseName, BaseDir, SubDirs) ->
+    lists:member("ebin", SubDirs) andalso
+        lists:member("src", SubDirs) andalso
+        sin_utils:file_exists(Config,
+                              filename:join([BaseDir, "ebin",
+                                             BaseName ++
+                                                 ".app"])).
+
+-spec has_src_appsrc(sin_config:config(),
+                     string(), string(), [string()]) ->
+                            boolean().
+has_src_appsrc(Config, BaseName, BaseDir, SubDirs) ->
+    lists:member("src", SubDirs) andalso
+        sin_utils:file_exists(Config,
+                              filename:join([BaseDir, "src",
+                                             BaseName ++
+                                                 ".app.src"])).
+
 
 %% @doc Gather the list of modules that currently may need to be built.
 gather_modules(SrcDir) ->
diff --git a/src/sin_task.erl b/src/sin_task.erl
index 7a4bc9c..5e65392 100644
--- a/src/sin_task.erl
+++ b/src/sin_task.erl
@@ -50,6 +50,7 @@ get_task_list(Config, TaskName) ->
 -spec get_tasks() -> [record(task)].
 get_tasks() ->
     [sin_task_depends:description(),
+     sin_task_prepare:description(),
      sin_task_version:description(),
      sin_task_test:description(),
      sin_task_shell:description(),
diff --git a/src/sin_task_build.erl b/src/sin_task_build.erl
index 4a928af..aa4a21e 100644
--- a/src/sin_task_build.erl
+++ b/src/sin_task_build.erl
@@ -31,7 +31,7 @@
 
 -define(SIGNS, "moddeps").
 -define(TASK, build).
--define(DEPS, [depends]).
+-define(DEPS, [prepare, depends]).
 
 %%====================================================================
 %% API
@@ -186,43 +186,33 @@ build_apps(BuildRef, BuildSupInfo, AppList, Args) ->
 
 %% @doc Build an individual otp application.
 build_app(BuildRef, Cache, Env, AppName, Args) ->
-    AppVsn = sin_config:get_value(BuildRef, "apps." ++ AppName ++ ".vsn"),
+    AppBuildDir =
+        sin_config:get_value(BuildRef, "apps." ++ AppName ++ ".builddir"),
     AppDir = sin_config:get_value(BuildRef, "apps." ++ AppName
-                                        ++ ".basedir"),
-    BuildTarget = lists:flatten([AppName, "-", AppVsn]),
-    AppBuildDir = filename:join([Env#env.apps_build_dir, BuildTarget]),
-    BuildRef2 = sin_config:store(BuildRef, "apps." ++ AppName ++ ".builddir",
-                                       AppBuildDir),
-    Target = filename:join([AppBuildDir, "ebin"]),
-
-    Ignorables = sin_config:get_value(BuildRef2, "ignore_dirs", []),
-
-    % Ignore the build dir when copying or we will create a deep monster in a
-    % few builds
-    BuildDir = sin_config:get_value(BuildRef2, "build_dir"),
-    sin_utils:copy_dir(BuildRef2, AppBuildDir, AppDir, "", [BuildDir | 
Ignorables]),
+                                  ++ ".basedir"),
 
+    Target = filename:join([AppBuildDir, "ebin"]),
 
-    {EbinPaths, Includes} = setup_code_path(BuildRef2, Env, AppName),
+    {EbinPaths, Includes} = setup_code_path(BuildRef, Env, AppName),
 
     code:add_patha(Target),
-    {NewCache, NewFileList} = process_source_files(BuildRef2,
+    {NewCache, NewFileList} = process_source_files(BuildRef,
                                                     Cache,
                                                     Env#env.build_dir,
                                                     Target,
                                                     Includes,
                                                     gather_modules(BuildRef,
                                                                    AppName)),
-    BuildRef3 = sin_config:store(BuildRef2,
+    BuildRef2 = sin_config:store(BuildRef,
                                  [{"apps." ++ AppName ++ ".code_paths",
                                   [Target | EbinPaths]},
                                   {"apps." ++ AppName ++ ".file_list",
                                    NewFileList}]),
 
-    build_sources(BuildRef3, NewFileList,
+    build_sources(BuildRef2, NewFileList,
                   Includes, Args, AppDir, Target),
 
-    {NewCache, BuildRef3}.
+    {NewCache, BuildRef2}.
 
 %% @doc go through each source file building with the correct build module.
 -spec build_sources(sin_config:config(), [tuple()], [string()],
diff --git a/src/sin_task_prepare.erl b/src/sin_task_prepare.erl
new file mode 100644
index 0000000..215ef68
--- /dev/null
+++ b/src/sin_task_prepare.erl
@@ -0,0 +1,90 @@
+%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
+%%%---------------------------------------------------------------------------
+%%% @author Eric Merritt
+%%% @doc
+%%% Prepare the apps for building in the build area
+%%% @end
+%%% @copyright (C) 2011 Erlware, LLC.
+%%%---------------------------------------------------------------------------
+-module(sin_task_prepare).
+
+-behaviour(sin_task).
+
+-include("internal.hrl").
+
+%% API
+-export([description/0,
+         do_task/1,
+         format_exception/1]).
+
+-define(TASK, prepare).
+-define(DEPS, [depends]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%% @doc provide a description of the system for the caller
+-spec description() -> sin_task:task_description().
+description() ->
+    Desc = "Prepares the build area for the rest of the tasks "
+        "that occur in the system",
+    #task{name = ?TASK,
+          task_impl = ?MODULE,
+          bare = false,
+          example = "prepare",
+          short_desc = "build area preparation",
+          deps = ?DEPS,
+          desc = Desc,
+          opts = []}.
+
+%% @doc do the system preparation
+-spec do_task(sin_config:config()) -> sin_config:config().
+do_task(BuildRef) ->
+    BuildDir = sin_config:get_value(BuildRef, "build.dir"),
+    Ignorables = sin_config:get_value(BuildRef, "ignore_dirs", []),
+
+    ProjectApps = sin_config:get_value(BuildRef, "project.allapps", []),
+    lists:foldl(fun(App, Config1) ->
+                          prepare_app(Config1, BuildDir, App, Ignorables)
+                  end, BuildRef, ProjectApps).
+
+-spec prepare_app(sin_config:config(), string(), string(),
+                  [string()]) ->
+                         sin_config:config().
+prepare_app(BuildRef0, BuildDir, AppInfo, Ignorables) ->
+    {AppName, _AppVsn, _Deps, AppBuildDir} = AppInfo,
+
+    AppStrName = erlang:atom_to_list(AppName),
+    AppDir = sin_config:get_value(BuildRef0, "apps." ++ AppStrName
+                                        ++ ".basedir"),
+
+    %% Ignore the build dir when copying or we will create a deep monster in a
+    %% few builds
+    sin_utils:copy_dir(BuildRef0, AppBuildDir, AppDir, "",
+                       [BuildDir | Ignorables]),
+
+    BuildRef1 = sin_config:store(BuildRef0, "apps." ++ AppStrName ++ 
".builddir",
+                                 AppBuildDir),
+
+    BaseDetails = sin_config:get_value(BuildRef1,
+                                       "apps." ++ AppStrName ++ ".base"),
+
+    DotApp = filename:join([AppBuildDir, "ebin", AppStrName ++ ".app"]),
+
+
+    ok = file:write_file(DotApp,
+                    io_lib:format("~p.\n",
+                                  [{application, AppName,
+                                    BaseDetails}])),
+    BuildRef1.
+
+%% @doc Format an exception thrown by this module
+-spec format_exception(sin_exceptions:exception()) ->
+    string().
+format_exception(Exception) ->
+    sin_exceptions:format_exception(Exception).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/sin_app_src.erl b/test/sin_app_src.erl
new file mode 100644
index 0000000..e85c7dc
--- /dev/null
+++ b/test/sin_app_src.erl
@@ -0,0 +1,120 @@
+-module(sin_app_src).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-export([given/3, 'when'/3, then/3]).
+
+given([a, generated, project, that, contains, an, 'ebin/app'],
+      _State, _) ->
+    {ok, BaseDir} = ewl_file:create_tmp_dir("/tmp"),
+    ProjectName = "super_foo",
+    {ProjectDir, _} =
+        sin_test_project_gen:single_app_project(BaseDir, ProjectName),
+    {ok, {ProjectDir, ProjectName}};
+given([a,generated,project,that,contains,an,'app.src'], _State, _) ->
+      {ok, BaseDir} = ewl_file:create_tmp_dir("/tmp"),
+    ProjectName = "super_foo",
+    {ProjectDir, _} =
+        sin_test_project_gen:single_app_project(BaseDir, ProjectName),
+    AppSrcPath = filename:join([ProjectDir, "src",
+                                ProjectName ++ ".app.src"]),
+    ?assertMatch(ok, file:write_file(AppSrcPath, app_src(ProjectName))),
+    {ok, {ProjectDir, ProjectName}};
+given([does,'not',contain,an,'ebin/app'],
+      State = {ProjectDir, ProjectName}, _) ->
+    AppSrc = filename:join([ProjectDir, "ebin", ProjectName ++ ".app"]),
+    ?assertMatch(ok, delete_if_exists(AppSrc)),
+    {ok, State};
+given([does,'not',contain,an,'app.src'],
+      State = {ProjectDir, ProjectName}, _) ->
+    AppSrc = filename:join([ProjectDir, "src", ProjectName ++ ".app.src"]),
+    ?assertMatch(ok, delete_if_exists(AppSrc)),
+    {ok, State};
+given([contains,an,'ebin/app'],
+      State = {ProjectDir, ProjectName}, _) ->
+    %% Generated by default so lets just make sure it exists
+    AppSrc = filename:join([ProjectDir, "ebin", ProjectName ++ ".app"]),
+    ?assertMatch(true,
+                 sin_utils:file_exists(sin_config:new(), AppSrc)),
+    {ok, State}.
+
+'when'([a, build, step, is, run, on, this, project],
+       {ProjectDir, ProjectName}, _) ->
+    Ret = sinan:run_sinan(["-s", ProjectDir, "build"]),
+    ?assertMatch({_, _}, Ret),
+    {_, TrueRet} = Ret,
+    {ok, {ProjectDir, ProjectName, TrueRet}}.
+
+then([build, the, app, normally], State = {_, _, BuildState}, _) ->
+    ?assertMatch([], sin_config:get_run_errors(BuildState)),
+    {ok, State};
+then([the, build, should, fail], State = {_, _, BuildState}, _) ->
+    ?assertMatch(1,  erlang:length(sin_config:get_run_errors(BuildState))),
+    {ok, State};
+then([sinan, should, put, the, app, file, in, 'ebin/.app'],
+     State = {_, ProjectName, BuildState}, _) ->
+    verify_ebin_app(ProjectName, BuildState),
+    {ok, State};
+then([sinan, should, warn, the, user, that,
+      both, an, 'ebin/app', 'and', 'app.src', exists],
+     State = {ProjectDir, _, BuildState}, _) ->
+    ?assertMatch(
+       [{sin_discover,
+         {sin_discover, _,
+          {"conflict: ~s has both an ebin/*.app and a src/*.app.src ",
+           [ProjectDir]}}}],
+       sin_config:get_run_errors(BuildState)),
+    {ok, State};
+then([warn, the, user, that, the,
+      'ebin/app', is, being, ignored],
+     State = {_, _, BuildState}, _) ->
+    Warnings = sin_config:get_run_warnings(BuildState),
+    ?assertMatch(true,
+                 lists:any(fun({sin_discover, Warning}) ->
+                                   case Warning of
+                                       "Unexpected ebin/.app overriding with 
src/app.src" ->
+                                           true;
+                                       _ ->
+                                           false
+                                   end;
+                              (_) ->
+                                   false
+                           end, Warnings)),
+                 {ok, State}.
+
+app_src(Name) ->
+    ["%% This is the application resource file (.app file) for the app2,\n"
+     "%% application.\n"
+     "{application, ", Name, ",\n"
+     "[{description, \"Your Desc HERE\"},\n"
+     "  {vsn, \"0.1.0\"},\n"
+     "  {modules, [", Name, "_app,\n"
+     "             ", Name, "_sup]},\n"
+     "  {registered,[", Name, "_sup]},\n"
+     "  {applications, [kernel, stdlib]},\n"
+     "  {mod, {", Name, "_app,[]}}, \n"
+     "  {start_phases, []}]}.\n"].
+
+delete_if_exists(Path) ->
+    case file:delete(Path) of
+        ok ->
+            ok;
+        {error, enoent} ->
+            ok;
+        Error ->
+            throw(Error)
+    end.
+
+verify_ebin_app(ProjectName, BuildState) ->
+    BaseDir = sin_config:get_value(BuildState,
+                                   "apps." ++ ProjectName ++ ".builddir"),
+    BasePath = filename:join([BaseDir, "ebin", ProjectName ++
+                                  ".app"]),
+    ?assertMatch(true,
+                 sin_utils:file_exists(sin_config:new(), BasePath)),
+    AppContents = file:consult(BasePath),
+    AtomName =  erlang:list_to_atom(ProjectName),
+    ?assertMatch({ok, [{application, AtomName, _}]}, AppContents),
+    {ok, [{_, _, Details}]} = AppContents,
+    ?assertMatch({vsn, "0.1.0"}, lists:keyfind(vsn, 1, Details)),
+    Details.
-- 
1.7.5.2

-- 
You received this message because you are subscribed to the Google Groups 
"erlware-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/erlware-dev?hl=en.

Reply via email to