%% =====================================================================
%% This library is free software; you can redistribute it and/or modify
%% it under the terms of the GNU Lesser General Public License as
%% published by the Free Software Foundation; either version 2 of the
%% License, or (at your option) any later version.
%%
%% This library is distributed in the hope that it will be useful, but
%% WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%% Lesser General Public License for more details.
%%
%% You should have received a copy of the GNU Lesser General Public
%% License along with this library; if not, write to the Free Software
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
%% @copyright 2003 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @see edoc
%% @end
%% =====================================================================
%% @doc Interface for calling EDoc from Erlang startup options.
%%
%% The following is an example of typical usage in a Makefile:
%% ```docs:
%% erl -noshell -run edoc_run application "'$(APP_NAME)'" \
%% '"."' '[{def,{vsn,"$(VSN)"}}]'
%% '''
%% (note the single-quotes to avoid shell expansion, and the
%% double-quotes enclosing the strings).
%%
%% <strong>New feature in version 0.6.9</strong>: It is no longer
%% necessary to write `-s init stop' last on the command line in order
%% to make the execution terminate. The termination (signalling success
%% or failure to the operating system) is now built into these
%% functions.
-module(edoc_run).
-export([file/1, application/1, files/1, toc/1]).
-compile({no_auto_import,[error/1]}).
-import(edoc_report, [report/2, error/1]).
-type args() :: [string()].
%% @spec application([string()]) -> none()
%%
%% @doc Calls {@link edoc:application/3} with the corresponding
%% arguments. The strings in the list are parsed as Erlang constant
%% terms. The list can be either `[App]', `[App, Options]' or `[App,
%% Dir, Options]'. In the first case {@link edoc:application/1} is
%% called instead; in the second case, {@link edoc:application/2} is
%% called.
%%
%% The function call never returns; instead, the emulator is
%% automatically terminated when the call has completed, signalling
%% success or failure to the operating system.
-spec application(args()) -> no_return().
application(Args) ->
F = fun () ->
case parse_args(Args) of
[App] -> edoc:application(App);
[App, Opts] -> edoc:application(App, Opts);
[App, Dir, Opts] -> edoc:application(App, Dir, Opts);
_ ->
invalid_args("edoc_run:application/1", Args)
end
end,
run(F).
%% @spec files([string()]) -> none()
%%
%% @doc Calls {@link edoc:files/2} with the corresponding arguments. The
%% strings in the list are parsed as Erlang constant terms. The list can
%% be either `[Files]' or `[Files, Options]'. In the first case, {@link
%% edoc:files/1} is called instead.
%%
%% The function call never returns; instead, the emulator is
%% automatically terminated when the call has completed, signalling
%% success or failure to the operating system.
-spec files(args()) -> no_return().
files(Args) ->
F = fun () ->
case parse_args(Args) of
[Files] -> edoc:files(Files);
[Files, Opts] -> edoc:files(Files, Opts);
_ ->
invalid_args("edoc_run:files/1", Args)
end
end,
run(F).
%% @hidden Not official yet
-spec toc(args()) -> no_return().
toc(Args) ->
F = fun () ->
case parse_args(Args) of
[Dir, Paths] -> edoc:toc(Dir,Paths);
[Dir, Paths, Opts] -> edoc:toc(Dir,Paths,Opts);
_ ->
invalid_args("edoc_run:toc/1", Args)
end
end,
run(F).
%% @spec file([string()]) -> none()
%%
%% @deprecated This is part of the old interface to EDoc and is mainly
%% kept for backwards compatibility. The preferred way of generating
%% documentation is through one of the functions {@link application/1}
%% and {@link files/1}.
%%
%% @doc Calls {@link edoc:file/2} with the corresponding arguments. The
%% strings in the list are parsed as Erlang constant terms. The list can
%% be either `[File]' or `[File, Options]'. In the first case, an empty
%% list of options is passed to {@link edoc:file/2}.
%%
%% The following is an example of typical usage in a Makefile:
%% ```$(DOCDIR)/%.html:%.erl
%% erl -noshell -run edoc_run file '"$<"' '[{dir,"$(DOCDIR)"}]' \
%% -s init stop'''
%%
%% The function call never returns; instead, the emulator is
%% automatically terminated when the call has completed, signalling
%% success or failure to the operating system.
-spec file(args()) -> no_return().
file(Args) ->
F = fun () ->
case parse_args(Args) of
[File] -> edoc:file(File, []);
[File, Opts] -> edoc:file(File, Opts);
_ ->
invalid_args("edoc_run:file/1", Args)
end
end,
run(F).
-spec invalid_args(string(), args()) -> no_return().
invalid_args(Where, Args) ->
report("invalid arguments to ~ts: ~w.", [Where, Args]),
shutdown_error().
run(F) ->
wait_init(),
case catch {ok, F()} of
{ok, _} ->
shutdown_ok();
{'EXIT', E} ->
report("edoc terminated abnormally: ~P.", [E, 10]),
shutdown_error();
Thrown ->
report("internal error: throw without catch in edoc: ~P.",
[Thrown, 15]),
shutdown_error()
end.
wait_init() ->
case erlang:whereis(code_server) of
undefined ->
erlang:yield(),
wait_init();
_ ->
ok
end.
%% When and if a function init:stop/1 becomes generally available, we
%% can use that instead of delay-and-pray when there is an error.
-spec shutdown_ok() -> no_return().
shutdown_ok() ->
%% shut down emulator nicely, signalling "normal termination"
init:stop().
-spec shutdown_error() -> no_return().
shutdown_error() ->
%% delay 1 second to allow I/O to finish
receive after 1000 -> ok end,
%% stop emulator the hard way with a nonzero exit value
halt(1).
parse_args([A | As]) when is_atom(A) ->
[parse_arg(atom_to_list(A)) | parse_args(As)];
parse_args([A | As]) ->
[parse_arg(A) | parse_args(As)];
parse_args([]) ->
[].
parse_arg(A) ->
case catch {ok, edoc_lib:parse_expr(A, 1)} of
{ok, Expr} ->
case catch erl_parse:normalise(Expr) of
{'EXIT', _} ->
report("bad argument: '~ts':", [A]),
exit(error);
Term ->
Term
end;
{error, _, D} ->
report("error parsing argument '~ts'", [A]),
error(D),
exit(error)
end.