Permalink
Browse files

Merge remote-tracking branch 'canonical/next'

  • Loading branch information...
2 parents 4d44153 + 9cd6dbc commit b0df56842dd3ca8c7c8cda3affce3e86c9924326 @ericbmerritt committed May 16, 2012
View
@@ -2,4 +2,5 @@ _build
erl_crash.dump
*.pyc
doc/_site
-.pc
+.pc
+*.beam
View
@@ -21,6 +21,7 @@ Dependencies
* [cucumberl](https://github.com/membase/cucumberl)
* [getopt](https://github.com/jcomellas/getopt)
* [proper](https://github.com/manopapad/proper)
+* [joxa](https://github.com/erlware/joxa)
The versions of these OTP Applications that are required are detailed
in the sinan.config located in the top level of the sinan project. If
View
@@ -1,7 +1,10 @@
-VSN=4.0.0
+VSN=4.1.0
ERLC=/usr/bin/env erlc
ERL=/usr/bin/env erl
-APPDIR= $(abspath ./_build/sinan/lib/sinan-$(VSN))
+
+RELDIR=$(CURDIR)/_build/sinan
+APPDIR=$(RELDIR)/lib/sinan-$(VSN)
+LOGDIR=$(RELDIR)/logs
BINDIR=$(DESTDIR)/usr/bin
INSTALL_TARGET=$(DESTDIR)/usr/lib/erlang/lib/sinan-$(VSN)
@@ -45,7 +48,7 @@ build_behaviours: $(BEHAVIOURS)
+warn_obsolete_guard \
+warnings_as_errors +bin_opt_info +debug_info -W -o $(BEAMDIR) $(BEHAVIOURS)
-main: setup build_behaviours ${ERL_OBJ} ${ERL_TEST_OBJ}
+ main: setup build_behaviours ${ERL_OBJ} ${ERL_TEST_OBJ}
$(BEAMDIR)/%.beam: %.erl
$(ERLC) -pa $(BEAMDIR) +warn_export_vars +warn_export_all \
@@ -81,6 +84,9 @@ smoketests: main
PYTHONPATH=$(PYPATH) python $$f ; \
done
+ct: main $(LOGDIR)
+ ct_run -dir $(CURDIR)/test -logdir $(LOGDIR) -erl_args -pa $(BEAMDIR)
+
testall : cucumber proper eunit smoketests
gh-pages:
@@ -112,6 +118,10 @@ update-version:
git commit -m "Version bump $(VSN)"
git tag v$(VSN)
+$(LOGDIR):
+ mkdir -p $(LOGDIR)
+
+
##
## Debian packaging support for sinan
##
View
@@ -2,7 +2,7 @@
{application, sinan,
[{description, "Build system for erlang"},
- {vsn, "4.0.0"},
+ {vsn, "4.1.0"},
{modules, []},
{registered, [sin_sup]},
{applications, [kernel, stdlib, compiler, erlware_commons,
View
@@ -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
@@ -1,5 +1,5 @@
{project_name, sinan}.
-{project_vsn, "4.0.0"}.
+{project_vsn, "4.1.0"}.
{escript, [{include_apps,
[erlware_commons, cucumberl, proper,
View
@@ -12,57 +12,225 @@
-include_lib("sinan/include/sinan.hrl").
%% API
--export([populate/2,
+-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
%%====================================================================
+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])).
-%% @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(State1, App)
- end, State0, ProjectApps).
-
--spec prepare_app(sin_state:state(), string()) ->
- sin_config:config().
-prepare_app(State0, #app{name=AppName, path=AppBuildDir}) ->
-
- BaseDetails = populate_modules(State0, AppName),
-
- DotApp = filename:join([AppBuildDir, "ebin",
- erlang:atom_to_list(AppName) ++ ".app"]),
+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}.
- ok = file:write_file(DotApp,
- io_lib:format("~p.\n",
- [{application, AppName,
- BaseDetails}])),
- State0.
+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
%%====================================================================
-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)),
-
- lists:reverse(
- lists:foldl(fun(Element = {vsn, _}, Acc) ->
- [{modules, SourceModuleList}, Element | Acc];
- (Element, Acc) ->
- [Element | Acc]
- end, [], lists:keydelete(modules, 1, Details))).
+rewrite_vsn(Config, State, AppDir, BaseDetails) ->
+ RWVSN = fun({vsn, VsnSpec}) ->
+ {vsn, vcs_vsn(Config, State, VsnSpec, AppDir)};
+ (El) -> El
+ end,
+ [RWVSN(Detail) || Detail <- BaseDetails].
+
+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
+ {rm_v, Cmd} ->
+ strip_v(get_vsn(Config, State, Cmd, Dir));
+ Cmd ->
+ get_vsn(Config, State, Cmd, Dir)
+ end.
+
+strip_v([$v | Vsn]) ->
+ Vsn;
+strip_v([$V | Vsn]) ->
+ Vsn;
+strip_v(Vsn) ->
+ Vsn.
+
+get_vsn(_Config, _State, {explicit_vsn, VsnString}, _Dir) ->
+ VsnString;
+get_vsn(Config, State, {cmd, CmdString}, Dir) ->
+ vcs_vsn_invoke(Config, State, CmdString, Dir);
+get_vsn(Config, State, Cmd, Dir) ->
+ vcs_vsn_invoke(Config, State, Cmd, Dir).
+
+vcs_vsn_cmd({rm_v, Vcs}) ->
+ {rm_v, vcs_vsn_cmd(Vcs)};
+vcs_vsn_cmd(git) ->
+ %% Explicitly git-describe a committish to accommodate for projects
+ %% in subdirs which don't have a GIT_DIR. In that case we will
+ %% get a description of the last commit that touched the subdir.
+ case os:type() of
+ {win32,nt} ->
+ "FOR /F \"usebackq tokens=* delims=\" %i in "
+ "(`git log -n 1 \"--pretty=format:%h\" .`) do "
+ "@git describe --always --tags %i";
+ _ ->
+ "git describe --always --tags `git log -n 1 --pretty=format:%h .`"
+ end;
+vcs_vsn_cmd(hg) -> "hg identify -i";
+vcs_vsn_cmd(bzr) -> "bzr revno";
+vcs_vsn_cmd(svn) -> "svnversion";
+vcs_vsn_cmd({cmd, _Cmd}=Custom) -> Custom;
+vcs_vsn_cmd(Version) -> {explicit_vsn, Version}.
+
+vcs_vsn_invoke(Config, State, Cmd, Dir) ->
+ case sin_sh:sh(Config, Cmd, [{cd, Dir}]) of
+ {ok, VsnString} ->
+ string:strip(VsnString, right, $\n);
+ {error, Error} ->
+ ?SIN_RAISE(State, {error_executing, Cmd, Error})
+ end.
View
@@ -25,6 +25,7 @@ get_target(BuildDir, File, ".jxa") ->
sin_file_info:mod(), [term()], string()) ->
{module(), sin_config:config()}.
build_file(Config, State, Module=#module{path=File}, Options, _Target) ->
+ sin_task:ensure_started(proper),
sin_log:verbose(Config, "Building ~s", [File]),
case joxa.compiler:'do-compile'(File, Options) of
Ctx when is_tuple(Ctx), element(1, Ctx) == context ->
View
@@ -246,7 +246,7 @@ new_limits() -> [].
add_limit(Config, State, Source, AppsLimits, NewLimit) ->
case is_valid_limit(NewLimit) of
false ->
- sin_config:normal(Config, "Invalid constraint ~p found originating at ~p",
+ sin_log:normal(Config, "Invalid constraint ~p found originating at ~p",
[NewLimit, Source]),
?SIN_RAISE(State, {invalid_constraint, NewLimit, Source});
true ->
Oops, something went wrong.

0 comments on commit b0df568

Please sign in to comment.