Permalink
Browse files

Add reltool based release support

Running "rebar generate" now creates a self-contained yaws system
under the build directory's "rel" subdirectory. The yaws script it
provides at ./rel/yaws/bin/yaws isn't the same as the normal yaws
script (the one normally found at ./bin/yaws); rather, it's a special
script that starts yaws and the Erlang applications on which it
depends as a local self-contained Erlang node. You can run

./rel/yaws/bin/yaws console

to start an interactive yaws node, or

./rel/yaws/bin/yaws start

to run it as a daemon, which you can later stop with

./rel/yaws/bin/yaws stop

This script does not accept the command-line options that ./bin/yaws
does, at least for now. If this is a problem, please raise an issue at
the Yaws github repo or on the Yaws mailing list.
  • Loading branch information...
tuncer authored and vinoski committed Dec 19, 2011
1 parent 1f8b315 commit b6b9d878397ac25eeefb61d64338c245f8ce576e
Showing with 538 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +2 −0 rebar.config
  3. +94 −0 rebar_reltool_link.erl
  4. +34 −0 rel/files/erl
  5. +138 −0 rel/files/nodetool
  6. +11 −0 rel/files/sys.config
  7. +19 −0 rel/files/vm.args
  8. +186 −0 rel/files/yaws
  9. +53 −0 rel/reltool.config
View
@@ -67,3 +67,4 @@ doc/yaws_head.pdf
ebin/yaws.appup
etc
yaws_logs
+rel/yaws
View
@@ -1,5 +1,7 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ft=erlang ts=4 sw=4 et
+{plugins, [rebar_reltool_link]}.
+{sub_dirs, ["rel"]}.
{erl_opts, [{platform_define,
"(linux|freebsd|darwin)", 'HAVE_SENDFILE'},
View
@@ -0,0 +1,94 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% Copyright (c) 2011 Dmitry Demeschuk
+%% Copyright (c) 2011 Tuncer Ayaz
+%%
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%% of this software and associated documentation files (the "Software"), to deal
+%% in the Software without restriction, including without limitation the rights
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the Software is
+%% furnished to do so, subject to the following conditions:
+%%
+%% The above copyright notice and this permission notice shall be included in
+%% all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%% THE SOFTWARE.
+%% -------------------------------------------------------------------
+-module(rebar_reltool_link).
+
+-export([pre_generate/2, post_generate/2]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+pre_generate(_, _) ->
+ %% Create symlinks to the current directory in the ?FAKE_LIB_DIR
+ %% directory to include the root application to the release. Works
+ %% for UNIX systems only.
+ case os:type() of
+ {unix, _} -> maybe_symlink_root_dir();
+ _ -> ok
+ end.
+
+post_generate(_, _) ->
+ case has_symlink_dir() of
+ {true, SymlinkDir} -> rebar_file_utils:rm_rf(SymlinkDir);
+ false -> ok
+ end.
+
+%% ===================================================================
+%% Internal functions
+%% ===================================================================
+
+-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
+-define(FAKE_LIB_DIR, "fake_lib_dir").
+-define(FAKE_LIB_DIR2, "../fake_lib_dir").
+
+maybe_symlink_root_dir() ->
+ case rebar_app_utils:is_app_dir("..") of
+ {true, RootAppSrc} ->
+ RootApp = rebar_app_utils:app_name(RootAppSrc),
+ SymlinkDir = filename:join(["..", ?FAKE_LIB_DIR, RootApp]),
+ case filelib:is_dir(SymlinkDir) of
+ false ->
+ rebar_utils:sh(?FMT("mkdir -p ~s", [SymlinkDir]), []);
+ true ->
+ ok
+ end,
+ Files = [F || F <- filelib:wildcard("../*"), F =/= ?FAKE_LIB_DIR2],
+ [begin
+ Filename = filename:basename(File),
+ Source = filename:absname(filename:join([File])),
+ Target = filename:join([SymlinkDir, Filename]),
+ rebar_utils:sh(?FMT("ln -Fs ~s ~s", [Source, Target]), [])
+ end
+ || File <- Files],
+ ok;
+ false ->
+ ok
+ end.
+
+has_symlink_dir() ->
+ case rebar_app_utils:is_app_dir("..") of
+ {true, RootAppSrc} ->
+ RootApp = rebar_app_utils:app_name(RootAppSrc),
+ SymlinkDir = filename:join(["..", ?FAKE_LIB_DIR, RootApp]),
+ case filelib:is_dir(SymlinkDir) of
+ true -> {true, SymlinkDir};
+ false -> false
+ end;
+ false ->
+ false
+ end.
View
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+## This script replaces the default "erl" in erts-VSN/bin. This is necessary
+## as escript depends on erl and in turn, erl depends on having access to a
+## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect
+## of running escript -- the embedded node bypasses erl and uses erlexec directly
+## (as it should).
+##
+## Note that this script makes the assumption that there is a start_clean.boot
+## file available in $ROOTDIR/release/VSN.
+
+# Determine the abspath of where this script is executing from.
+ERTS_BIN_DIR=$(cd ${0%/*} && pwd)
+
+# Now determine the root directory -- this script runs from erts-VSN/bin,
+# so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR
+# path.
+ROOTDIR=${ERTS_BIN_DIR%/*/*}
+
+# Parse out release and erts info
+START_ERL=`cat $ROOTDIR/releases/start_erl.data`
+ERTS_VSN=${START_ERL% *}
+APP_VSN=${START_ERL#* }
+
+BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin
+EMU=beam
+PROGNAME=`echo $0 | sed 's/.*\\///'`
+CMD="$BINDIR/erlexec"
+export EMU
+export ROOTDIR
+export BINDIR
+export PROGNAME
+
+exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"}
View
@@ -0,0 +1,138 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ft=erlang ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% nodetool: Helper Script for interacting with live nodes
+%%
+%% -------------------------------------------------------------------
+
+main(Args) ->
+ ok = start_epmd(),
+ %% Extract the args
+ {RestArgs, TargetNode} = process_args(Args, [], undefined),
+
+ %% See if the node is currently running -- if it's not, we'll bail
+ case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of
+ {true, pong} ->
+ ok;
+ {_, pang} ->
+ io:format("Node ~p not responding to pings.\n", [TargetNode]),
+ halt(1)
+ end,
+
+ case RestArgs of
+ ["ping"] ->
+ %% If we got this far, the node already responsed to a ping, so just dump
+ %% a "pong"
+ io:format("pong\n");
+ ["stop"] ->
+ io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]);
+ ["restart"] ->
+ io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]);
+ ["reboot"] ->
+ io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]);
+ ["rpc", Module, Function | RpcArgs] ->
+ case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function),
+ [RpcArgs], 60000) of
+ ok ->
+ ok;
+ {badrpc, Reason} ->
+ io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
+ halt(1);
+ _ ->
+ halt(1)
+ end;
+ ["rpcterms", Module, Function, ArgsAsString] ->
+ case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function),
+ consult(ArgsAsString), 60000) of
+ {badrpc, Reason} ->
+ io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]),
+ halt(1);
+ Other ->
+ io:format("~p\n", [Other])
+ end;
+ Other ->
+ io:format("Other: ~p\n", [Other]),
+ io:format("Usage: nodetool {ping|stop|restart|reboot}\n")
+ end,
+ net_kernel:stop().
+
+process_args([], Acc, TargetNode) ->
+ {lists:reverse(Acc), TargetNode};
+process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) ->
+ erlang:set_cookie(node(), list_to_atom(Cookie)),
+ process_args(Rest, Acc, TargetNode);
+process_args(["-name", TargetName | Rest], Acc, _) ->
+ ThisNode = append_node_suffix(TargetName, "_maint_"),
+ {ok, _} = net_kernel:start([ThisNode, longnames]),
+ process_args(Rest, Acc, nodename(TargetName));
+process_args(["-sname", TargetName | Rest], Acc, _) ->
+ ThisNode = append_node_suffix(TargetName, "_maint_"),
+ {ok, _} = net_kernel:start([ThisNode, shortnames]),
+ process_args(Rest, Acc, nodename(TargetName));
+process_args([Arg | Rest], Acc, Opts) ->
+ process_args(Rest, [Arg | Acc], Opts).
+
+
+start_epmd() ->
+ [] = os:cmd(epmd_path() ++ " -daemon"),
+ ok.
+
+epmd_path() ->
+ ErtsBinDir = filename:dirname(escript:script_name()),
+ Name = "epmd",
+ case os:find_executable(Name, ErtsBinDir) of
+ false ->
+ case os:find_executable(Name) of
+ false ->
+ io:format("Could not find epmd.~n"),
+ halt(1);
+ GlobalEpmd ->
+ GlobalEpmd
+ end;
+ Epmd ->
+ Epmd
+ end.
+
+
+nodename(Name) ->
+ case string:tokens(Name, "@") of
+ [_Node, _Host] ->
+ list_to_atom(Name);
+ [Node] ->
+ [_, Host] = string:tokens(atom_to_list(node()), "@"),
+ list_to_atom(lists:concat([Node, "@", Host]))
+ end.
+
+append_node_suffix(Name, Suffix) ->
+ case string:tokens(Name, "@") of
+ [Node, Host] ->
+ list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host]));
+ [Node] ->
+ list_to_atom(lists:concat([Node, Suffix, os:getpid()]))
+ end.
+
+
+%%
+%% Given a string or binary, parse it into a list of terms, ala file:consult/0
+%%
+consult(Str) when is_list(Str) ->
+ consult([], Str, []);
+consult(Bin) when is_binary(Bin)->
+ consult([], binary_to_list(Bin), []).
+
+consult(Cont, Str, Acc) ->
+ case erl_scan:tokens(Cont, Str, 0) of
+ {done, Result, Remaining} ->
+ case Result of
+ {ok, Tokens, _} ->
+ {ok, Term} = erl_parse:parse_term(Tokens),
+ consult([], Remaining, [Term | Acc]);
+ {eof, _Other} ->
+ lists:reverse(Acc);
+ {error, Info, _} ->
+ {error, Info}
+ end;
+ {more, Cont1} ->
+ consult(Cont1, eof, Acc)
+ end.
View
@@ -0,0 +1,11 @@
+[
+ %% SASL config
+ {sasl, [
+ {sasl_error_logger, {file, "log/sasl-error.log"}},
+ {errlog_type, error},
+ {error_logger_mf_dir, "log/sasl"}, % Log directory
+ {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size
+ {error_logger_mf_maxfiles, 5} % 5 files max
+ ]}
+].
+
View
@@ -0,0 +1,19 @@
+## Name of the node
+-name yaws@127.0.0.1
+
+## Cookie for distributed erlang
+-setcookie yaws
+
+## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
+## (Disabled by default..use with caution!)
+##-heart
+
+## Enable kernel poll and a few async threads
++K true
++A 5
+
+## Increase number of concurrent ports/sockets
+-env ERL_MAX_PORTS 4096
+
+## Tweak GC to run more often
+-env ERL_FULLSWEEP_AFTER 10
Oops, something went wrong.

0 comments on commit b6b9d87

Please sign in to comment.