---
ebin/sinan.app | 1 +
features/sin_programatic_gen.feature | 11 ++
features/sin_testability.feature | 2 +-
src/sin_gen.erl | 166 +++++++++++++++++++++++++++++++++
src/sin_task_gen.erl | 171 ++++------------------------------
test/sin_programatic_gen.erl | 31 ++++++
test/sin_test_project_gen.erl | 66 +++++++++++++
test/sin_testability.erl | 29 ++++--
8 files changed, 313 insertions(+), 164 deletions(-)
create mode 100644 features/sin_programatic_gen.feature
create mode 100644 src/sin_gen.erl
create mode 100644 test/sin_programatic_gen.erl
create mode 100644 test/sin_test_project_gen.erl
diff --git a/ebin/sinan.app b/ebin/sinan.app
index 3716c9f..fc9b4ae 100644
--- a/ebin/sinan.app
+++ b/ebin/sinan.app
@@ -25,6 +25,7 @@
sin_task_depends,
sin_task_shell,
sinan,
+ sin_gen,
sin_discover,
sin_sup,
sin_app,
diff --git a/features/sin_programatic_gen.feature
b/features/sin_programatic_gen.feature
new file mode 100644
index 0000000..30ded77
--- /dev/null
+++ b/features/sin_programatic_gen.feature
@@ -0,0 +1,11 @@
+Feature: Make the gen task of sinan more scriptable
+ In order to make it easier to test sinan from sinan
+ As an Erlang Developer
+ I want sinan to be able to run a project gen task pragmatically,
+ from erlang, without user input.
+
+ Scenario: Run gen without user input
+ Given an empty temp directory with no project
+ When gen is called pragmatically with out available user input
+ And a build is run
+ Then sinan should build the project normally
diff --git a/features/sin_testability.feature b/features/sin_testability.feature
index c4be6ac..a3a5fb3 100644
--- a/features/sin_testability.feature
+++ b/features/sin_testability.feature
@@ -1,7 +1,7 @@
Feature: Make sinan more testable
In order to make it easier to test sinan from sinan
As an Erlang Developer
- I want sinan to be able to pass the start directory to the a sinan
+ I want sinan to be able to pass the start directory to a sinan
call instead of having it inferred.
Scenario: Pass the start dir to a sinan project
diff --git a/src/sin_gen.erl b/src/sin_gen.erl
new file mode 100644
index 0000000..bda2ce6
--- /dev/null
+++ b/src/sin_gen.erl
@@ -0,0 +1,166 @@
+%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
+%%%---------------------------------------------------------------------------
+%%% @author Eric Merritt <[email protected]>
+%%% @doc
+%%% Provides utitlities to generate a complient otp/erlang
+%%% project when arguments are correctly passed to it.
+%%% @end
+%%% @copyright (C) 2007-2011 Erlware
+%%%---------------------------------------------------------------------------
+-module(sin_gen).
+
+%% API
+-export([gen/1]).
+
+%%============================================================================
+%% Types
+%%============================================================================
+
+-type env() :: [{Key::atom(), Value::term()}].
+
+%%============================================================================
+%% API
+%%============================================================================
+
+%% @doc Kicks off the generation process. Handles the individual steps in new
+%% project generation.
+-spec gen(sin_config:config()) -> ok.
+gen(Env) ->
+ build_out_skeleton(Env).
+
+%%============================================================================
+%% Internal functions
+%%============================================================================
+%% @doc Given the project directory builds out the various directories required
+%% for an application.
+-spec build_out_skeleton(env()) -> ok.
+build_out_skeleton(Env) ->
+ ProjDir = get_env(project_dir, Env),
+ make_dir(filename:join(ProjDir, "doc")),
+ make_dir(filename:join(ProjDir, "bin")),
+ make_dir(filename:join(ProjDir, "config")),
+ build_out_applications(Env).
+
+%% @doc Given the project directory and a list of application names, builds out
+%% the application directory structure.
+-spec build_out_applications(env()) -> ok.
+build_out_applications(Env) ->
+ case get_env(single_app_project, Env) of
+ false ->
+ Apps = get_env(apps, Env),
+ build_out_applications(Env, Apps);
+ true ->
+ ProjDir = get_env(project_dir, Env),
+ ProjVsn = get_env(project_version, Env),
+ ProjName = get_env(project_name, Env),
+ build_out_application(Env, ProjDir, ProjName, ProjVsn),
+ build_out_build_config(Env)
+ end.
+
+-spec build_out_applications(env(), AppNames::[string()]) -> ok.
+build_out_applications(Env, [AppName | T]) ->
+ ProjDir = get_env(project_dir, Env),
+ AppDir = filename:join([ProjDir, "lib", AppName]),
+ build_out_application(Env, AppDir, AppName, "0.1.0"),
+ build_out_applications(Env, T);
+build_out_applications(Env, []) ->
+ build_out_build_config(Env).
+
+%% @doc build out all the things required by an application
+-spec build_out_application(env(), AppDir::string(),
+ AppName::string(), AppVsn::string()) -> ok.
+build_out_application(Env, AppDir, AppName, AppVsn) ->
+ EbinDir = make_dir(filename:join(AppDir, "ebin")),
+ AppSrc = make_dir(filename:join(AppDir, "src")),
+ make_dir(filename:join(AppDir, "include")),
+ make_dir(filename:join(AppDir, "doc")),
+ build_out_super(Env, AppSrc, AppName),
+ build_out_app_src(Env, EbinDir, AppName, AppVsn),
+ build_out_otp(Env, AppSrc, AppName),
+ build_out_app_doc(Env, EbinDir, AppName).
+
+%% @doc Builds out the supervisor for the app.
+-spec build_out_super(env(), AppSrc::string(), AppName::string()) -> ok.
+build_out_super(Env, AppSrc, AppName) ->
+ FileName = filename:join(AppSrc, AppName ++ "_sup.erl"),
+ case filelib:is_file(FileName) of
+ true ->
+ ok;
+ false ->
+ sin_skel:supervisor(Env, FileName, AppName)
+ end.
+
+%% @doc Builds out the app descriptor for the app.
+-spec build_out_app_src(env(), EbinDir::string(),
+ AppName::string(), AppVsn::string()) -> ok.
+build_out_app_src(Env, EbinDir, AppName, AppVsn) ->
+ FileName = filename:join(EbinDir, AppName ++ ".app"),
+ case filelib:is_file(FileName) of
+ true ->
+ ok;
+ false ->
+ sin_skel:app_info(Env, FileName, AppName, AppVsn)
+ end.
+
+%% @doc Builds out the overview.edoc for the app.
+-spec build_out_app_doc(env(), EbinDir::string(), AppName::string()) -> ok.
+build_out_app_doc(Env, EbinDir, AppName) ->
+ FileName = filename:join(EbinDir, "overview.edoc"),
+ case filelib:is_file(FileName) of
+ true ->
+ ok;
+ false ->
+ sin_skel:edoc_overview(Env, FileName, AppName)
+ end.
+
+%% @doc Build out the top level otp parts of the application.
+-spec build_out_otp(env(), AppSrc::string(), AppName::string()) -> ok.
+build_out_otp(Env, AppSrc, AppName) ->
+ FileName = filename:join(AppSrc, AppName ++ "_app.erl"),
+ case filelib:is_file(FileName) of
+ true ->
+ ok;
+ false ->
+ sin_skel:application(Env, FileName, AppName)
+ end.
+
+%% @doc Builds the build config dir in the root of the project.
+build_out_build_config(Env) ->
+ case get_env(wants_build_config, Env) of
+ true ->
+ ProjectDir = get_env(project_dir, Env),
+ ProjectName = get_env(project_name, Env),
+ ConfName = filename:join([ProjectDir, "sinan.cfg"]),
+ ErlwareFile =
+ filename:join([ProjectDir, "bin",
+ "erlware_release_start_helper"]),
+ BinFile = filename:join([ProjectDir, "bin", ProjectName]),
+ ConfigFile = filename:join([ProjectDir, "config", "sys.config"]),
+ sin_skel:build_config(Env, ConfName),
+ sin_skel:bin(Env, BinFile),
+ sin_skel:bin_support(Env, ErlwareFile),
+ sin_skel:sysconfig(Env, ConfigFile);
+ false ->
+ ok
+ end.
+
+%% @doc Helper function that makes the specified directory and all parent
+%% directories.
+-spec make_dir(DirName::string()) -> ok.
+make_dir(DirName) ->
+ filelib:ensure_dir(DirName),
+ is_made(DirName, file:make_dir(DirName)),
+ DirName.
+
+%% @doc Helper function that makes sure a directory is made by testing the
+%% output of file:make_dir().
+is_made(DirName, ok) ->
+ ewl_talk:say("~s created ok.", [DirName]);
+is_made(DirName, {error, eexist}) ->
+ ewl_talk:say("~s created ok.", [DirName]).
+
+%% @doc Get the value from the environment.
+-spec get_env(Key::string(), env()) -> ok.
+get_env(Name, Env) ->
+ {value, {Name, Value}} = lists:keysearch(Name, 1, Env),
+ Value.
diff --git a/src/sin_task_gen.erl b/src/sin_task_gen.erl
index db0342a..9383417 100644
--- a/src/sin_task_gen.erl
+++ b/src/sin_task_gen.erl
@@ -100,13 +100,22 @@ get_project_information(Config, Env) ->
{project_dir, Dir},
{erts_version, ErtsVersion} | Env],
- case ewl_talk:ask_default("Is this a single application project",
- boolean, "n") of
- false ->
- get_application_names([{single_app_project, false} | Env2]);
- true ->
- build_out_skeleton([{single_app_project, true} | Env2])
- end,
+ Env3 =
+ case ewl_talk:ask_default("Is this a single application project",
+ boolean, "n") of
+ false ->
+ get_application_names([{single_app_project, false} | Env2]);
+ true ->
+ [{single_app_project, true} | Env2]
+ end,
+ Env4 = case ewl_talk:ask_default("Would you like a build config?",
+ boolean, "y") of
+ true ->
+ [{wants_build_config, true} | Env3];
+ false ->
+ [{wants_build_config, false} | Env3]
+ end,
+ all_done(sin_gen:gen(Env4)),
Config.
%% @doc Queries the user for a list of application names. The user can choose
@@ -121,8 +130,7 @@ get_application_names(Env) ->
-spec get_application_names(env(), [string()], []) -> [Names::string()].
get_application_names(Env, no_data, Acc) ->
- Env2 = [{apps, Acc} | Env],
- build_out_project(Env2);
+ [{apps, Acc} | Env];
get_application_names(Env, App, Acc) ->
NewAcc = case lists:member(App, Acc) of
true ->
@@ -133,150 +141,9 @@ get_application_names(Env, App, Acc) ->
end,
get_application_names(Env, ewl_talk:ask_default("app", ""), NewAcc).
-%% @doc Build out the project directory structure
--spec build_out_project(env()) -> ok.
-build_out_project(Env) ->
- ProjDir = get_env(project_dir, Env),
- make_dir(ProjDir),
- build_out_skeleton(Env).
-
-%% @doc Given the project directory builds out the various directories required
-%% for an application.
--spec build_out_skeleton(env()) -> ok.
-build_out_skeleton(Env) ->
- ProjDir = get_env(project_dir, Env),
- make_dir(filename:join(ProjDir, "doc")),
- make_dir(filename:join(ProjDir, "bin")),
- make_dir(filename:join(ProjDir, "config")),
- build_out_applications(Env).
-
-%% @doc Given the project directory and a list of application names, builds out
-%% the application directory structure.
--spec build_out_applications(env()) -> ok.
-build_out_applications(Env) ->
- case get_env(single_app_project, Env) of
- false ->
- Apps = get_env(apps, Env),
- build_out_applications(Env, Apps);
- true ->
- ProjDir = get_env(project_dir, Env),
- ProjVsn = get_env(project_version, Env),
- ProjName = get_env(project_name, Env),
- build_out_application(Env, ProjDir, ProjName, ProjVsn),
- build_out_build_config(Env)
- end.
-
--spec build_out_applications(env(), AppNames::[string()]) -> ok.
-build_out_applications(Env, [AppName | T]) ->
- ProjDir = get_env(project_dir, Env),
- AppDir = filename:join([ProjDir, "lib", AppName]),
- build_out_application(Env, AppDir, AppName, "0.1.0"),
- build_out_applications(Env, T);
-build_out_applications(Env, []) ->
- build_out_build_config(Env).
-
-%% @doc build out all the things required by an application
--spec build_out_application(env(), AppDir::string(),
- AppName::string(), AppVsn::string()) -> ok.
-build_out_application(Env, AppDir, AppName, AppVsn) ->
- EbinDir = make_dir(filename:join(AppDir, "ebin")),
- AppSrc = make_dir(filename:join(AppDir, "src")),
- make_dir(filename:join(AppDir, "include")),
- make_dir(filename:join(AppDir, "doc")),
- build_out_super(Env, AppSrc, AppName),
- build_out_app_src(Env, EbinDir, AppName, AppVsn),
- build_out_otp(Env, AppSrc, AppName),
- build_out_app_doc(Env, EbinDir, AppName).
-
-%% @doc Builds out the supervisor for the app.
--spec build_out_super(env(), AppSrc::string(), AppName::string()) -> ok.
-build_out_super(Env, AppSrc, AppName) ->
- FileName = filename:join(AppSrc, AppName ++ "_sup.erl"),
- case filelib:is_file(FileName) of
- true ->
- ok;
- false ->
- sin_skel:supervisor(Env, FileName, AppName)
- end.
-
-%% @doc Builds out the app descriptor for the app.
--spec build_out_app_src(env(), EbinDir::string(),
- AppName::string(), AppVsn::string()) -> ok.
-build_out_app_src(Env, EbinDir, AppName, AppVsn) ->
- FileName = filename:join(EbinDir, AppName ++ ".app"),
- case filelib:is_file(FileName) of
- true ->
- ok;
- false ->
- sin_skel:app_info(Env, FileName, AppName, AppVsn)
- end.
-
-%% @doc Builds out the overview.edoc for the app.
--spec build_out_app_doc(env(), EbinDir::string(), AppName::string()) -> ok.
-build_out_app_doc(Env, EbinDir, AppName) ->
- FileName = filename:join(EbinDir, "overview.edoc"),
- case filelib:is_file(FileName) of
- true ->
- ok;
- false ->
- sin_skel:edoc_overview(Env, FileName, AppName)
- end.
-
-%% @doc Build out the top level otp parts of the application.
--spec build_out_otp(env(), AppSrc::string(), AppName::string()) -> ok.
-build_out_otp(Env, AppSrc, AppName) ->
- FileName = filename:join(AppSrc, AppName ++ "_app.erl"),
- case filelib:is_file(FileName) of
- true ->
- ok;
- false ->
- sin_skel:application(Env, FileName, AppName)
- end.
-
-%% @doc Builds the build config dir in the root of the project.
-build_out_build_config(Env) ->
- case ewl_talk:ask_default("Would you like a build config?",
- boolean, "y") of
- true ->
- ProjectDir = get_env(project_dir, Env),
- ProjectName = get_env(project_name, Env),
- ConfName = filename:join([ProjectDir, "sinan.cfg"]),
- ErlwareFile =
- filename:join([ProjectDir, "bin",
- "erlware_release_start_helper"]),
- BinFile = filename:join([ProjectDir, "bin", ProjectName]),
- ConfigFile = filename:join([ProjectDir, "config", "sys.config"]),
- sin_skel:build_config(Env, ConfName),
- sin_skel:bin(Env, BinFile),
- sin_skel:bin_support(Env, ErlwareFile),
- sin_skel:sysconfig(Env, ConfigFile);
- false ->
- ok
- end,
- all_done().
%% @doc Prints out a nice error message if everything was ok.
--spec all_done() -> ok.
-all_done() ->
+-spec all_done(ok) -> ok.
+all_done(ok) ->
ewl_talk:say("Project was created, you should be good to go!").
-%% @doc Helper function that makes the specified directory and all parent
-%% directories.
--spec make_dir(DirName::string()) -> ok.
-make_dir(DirName) ->
- filelib:ensure_dir(DirName),
- is_made(DirName, file:make_dir(DirName)),
- DirName.
-
-%% @doc Helper function that makes sure a directory is made by testing the
-%% output of file:make_dir().
-is_made(DirName, ok) ->
- ewl_talk:say("~s created ok.", [DirName]);
-is_made(DirName, {error, eexist}) ->
- ewl_talk:say("~s created ok.", [DirName]).
-
-%% @doc Get the value from the environment.
--spec get_env(Key::string(), env()) -> ok.
-get_env(Name, Env) ->
- {value, {Name, Value}} = lists:keysearch(Name, 1, Env),
- Value.
diff --git a/test/sin_programatic_gen.erl b/test/sin_programatic_gen.erl
new file mode 100644
index 0000000..f738557
--- /dev/null
+++ b/test/sin_programatic_gen.erl
@@ -0,0 +1,31 @@
+%%%-------------------------------------------------------------------
+%%% @copyright (C) 2011, Erlware, LLC.
+%%% @doc
+%%% Test the ability to generate a project programatically
+%%% @end
+%%% Created : 5 Sep 2011 by Eric Merritt <[email protected]>
+%%%-------------------------------------------------------------------
+-module(sin_programatic_gen).
+
+-export([given/3, 'when'/3, then/3]).
+
+% Step definitions for the sample calculator Addition feature.
+
+given([an, empty, temp, directory, with, no, project], _State,
+ _) ->
+ {ok, BaseDir} = ewl_file:create_tmp_dir("/tmp"),
+ {ok, BaseDir}.
+
+'when'([gen, is, called, pragmatically, with, out,
+ available, user, input], BaseDir, _) ->
+ ProjectName = "super_foo",
+ {ProjectDir, _} =
+ sin_test_project_gen:single_app_project(BaseDir, ProjectName),
+ {ok, {ProjectDir, ProjectName}};
+'when'([a, build, is, run], State = {ProjectDir, _ProjectName}, _) ->
+ sinan:run_sinan(["-s", ProjectDir, "build"]),
+ {ok, State}.
+
+then([sinan, should, build, the, project, normally],
+ {ProjectDir, ProjectName}, _) ->
+ sin_test_project_gen:validate_single_app_project(ProjectDir, ProjectName).
diff --git a/test/sin_test_project_gen.erl b/test/sin_test_project_gen.erl
new file mode 100644
index 0000000..b21fcb6
--- /dev/null
+++ b/test/sin_test_project_gen.erl
@@ -0,0 +1,66 @@
+%%%-------------------------------------------------------------------
+%%% @copyright (C) 2011, Erlware, LLC.
+%%% @doc
+%%% Test the ability to generate a project programatically
+%%% @end
+%%% Created : 5 Sep 2011 by Eric Merritt <[email protected]>
+%%%-------------------------------------------------------------------
+-module(sin_test_project_gen).
+
+-export([single_app_project/2,
+ validate_single_app_project/2]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+single_app_project(BaseDir, ProjectName) ->
+ ProjectDir = filename:join([BaseDir, ProjectName]),
+ Env = [{year, "2010"},
+ {username, "ATestUser"},
+ {email_address, "[email protected]"},
+ {copyright_holder, "ATestUser"},
+ {project_version, "0.1.0"},
+ {project_name, ProjectName},
+ {project_dir, ProjectDir},
+ {erts_version, erlang:system_info(version)},
+ {single_app_project, true},
+ {wants_build_config, true}],
+ sin_gen:gen(Env),
+ {ProjectDir, Env}.
+
+validate_single_app_project(ProjectDir, ProjectName) ->
+ ?assertMatch(true, filelib:is_file(ProjectDir)),
+ Config = filename:join([ProjectDir, "config"]),
+ ?assertMatch(true, filelib:is_file(Config)),
+ ?assertMatch(true, filelib:is_regular(filename:join([Config,
"sys.config"]))),
+ Src = filename:join([ProjectDir, "src"]),
+ ?assertMatch(true, filelib:is_file(Src)),
+ ?assertMatch(true, filelib:is_regular(filename:join([Src, ProjectName ++
"_app.erl"]))),
+ ?assertMatch(true, filelib:is_regular(filename:join([Src, ProjectName ++
"_sup.erl"]))),
+ ?assertMatch(true, filelib:is_file(filename:join([ProjectDir,
"include"]))),
+ ?assertMatch(true, filelib:is_file(filename:join([ProjectDir, "doc"]))),
+ ?assertMatch(true, filelib:is_regular(filename:join([ProjectDir,
"sinan.cfg"]))),
+ Bin = filename:join([ProjectDir, "bin"]),
+ ?assertMatch(true, filelib:is_file(Bin)),
+ ?assertMatch(true, filelib:is_regular(filename:join([Bin, ProjectName]))),
+ EBin = filename:join([ProjectDir, "ebin"]),
+ ?assertMatch(true, filelib:is_file(EBin)),
+ ?assertMatch(true, filelib:is_regular(filename:join([EBin, ProjectName ++
".app"]))),
+ ?assertMatch(true, filelib:is_regular(filename:join([EBin,
"overview.edoc"]))),
+ AppDir = filename:join([ProjectDir, "_build", "development", "apps",
+ ProjectName ++ "-0.1.0"]),
+ ?assertMatch(true, filelib:is_file(AppDir)),
+ BConfig = filename:join([AppDir, "config"]),
+ ?assertMatch(true, filelib:is_file(BConfig)),
+ ?assertMatch(true, filelib:is_regular(filename:join([BConfig,
"sys.config"]))),
+ BSrc = filename:join([AppDir, "src"]),
+ ?assertMatch(true, filelib:is_file(BSrc)),
+ ?assertMatch(true, filelib:is_regular(filename:join([BSrc, ProjectName ++
"_app.erl"]))),
+ ?assertMatch(true, filelib:is_regular(filename:join([BSrc, ProjectName ++
"_sup.erl"]))),
+ ?assertMatch(true, filelib:is_file(filename:join([AppDir, "include"]))),
+ ?assertMatch(true, filelib:is_file(filename:join([AppDir, "doc"]))),
+ BEBin = filename:join([AppDir, "ebin"]),
+ ?assertMatch(true, filelib:is_file(BEBin)),
+ ?assertMatch(true, filelib:is_regular(filename:join([BEBin, ProjectName ++
"_app.beam"]))),
+ ?assertMatch(true, filelib:is_regular(filename:join([BEBin, ProjectName ++
"_sup.beam"]))),
+ ?assertMatch(true, filelib:is_regular(filename:join([BEBin, ProjectName ++
".app"]))),
+ ok.
diff --git a/test/sin_testability.erl b/test/sin_testability.erl
index fc87cca..0fd61ac 100644
--- a/test/sin_testability.erl
+++ b/test/sin_testability.erl
@@ -7,21 +7,28 @@
%%%-------------------------------------------------------------------
-module(sin_testability).
--export([given/2, 'when'/2, then/2, step/2]).
+-export([given/3, 'when'/3, then/3]).
% Step definitions for the sample calculator Addition feature.
-given([a, generated, project, in, a, different, location, then, the, 'CWD'],
- _) ->
- io:format("~p~n", [file:get_cwd()]).
+given([a, generated, project, in, a, different, location, then, the, cwd],
+ _, _) ->
+ {ok, BaseDir} = ewl_file:create_tmp_dir("/tmp"),
+ ProjectName = "foobachoo",
+ {ProjectDir, _} =
+ sin_test_project_gen:single_app_project(BaseDir, ProjectName),
+ {ok, {ProjectDir, ProjectName}}.
-'when'([a, build, step, is, run, on, this, project], _) ->
- ok;
-'when'([a, start, dir, is, passed, to, the, build], _) ->
- ok.
+'when'([a, build, step, is, run, on, this, project],
+ Ret = {ProjectDir, _}, _) ->
+ sinan:run_sinan(["-s", ProjectDir, "build"]),
+ {ok, Ret};
+'when'([a, start, dir, is, passed, to, the, build], Ret, _) ->
+ %% Nothing really to be done here since the start dir is passed when
+ %% The build is run.
+ {ok, Ret}.
then([sinan, should, build, the, project, in, the, location,
- specified, by, the, start, dir], _) ->
+ specified, by, the, start, dir], {ProjectDir, ProjectName}, _) ->
+ sin_test_project_gen:validate_single_app_project(ProjectDir, ProjectName),
ok.
-
-step(_, _) -> undefined.
--
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.