%% ===================================================================== %% Licensed under the Apache License, Version 2.0 (the "License"); you may %% not use this file except in compliance with the License. You may obtain %% a copy of the License at %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% Alternatively, you may use this file under the terms of the GNU Lesser %% General Public License (the "LGPL") as published by the Free Software %% Foundation; either version 2.1, or (at your option) any later version. %% If you wish to allow use of your version of this file only under the %% terms of the LGPL, you should delete the provisions above and replace %% them with the notice and other provisions required by the LGPL; see %% . If you do not delete the provisions %% above, a recipient may use your version of this file under the terms of %% either the Apache License or the LGPL. %% %% @copyright 2019-2021 Radek Szymczyszyn %% @author Radek Szymczyszyn %% @end %% ===================================================================== %% @doc Doclet generating standalone %% EEP-48 %% doc chunk files. %% %% Section Using the EDoc API %% in the EDoc User's Guide shows an example of using this module. %% @see //stdlib/shell_docs %% @see edoc_layout_chunks %% @end %% Note that this is written so that it is *not* depending on edoc.hrl! -module(edoc_doclet_chunks). -export([run/2]). -import(edoc_report, [report/2]). %% @headerfile "../include/edoc_doclet.hrl" -include("../include/edoc_doclet.hrl"). -define(DEFAULT_FILE_SUFFIX, ".chunk"). -define(CHUNKS_DIR, "chunks"). -include_lib("xmerl/include/xmerl.hrl"). -define(debug(Format, Args), ok). %-define(debug(Format, Args), io:format(Format, Args)). %% @doc Main doclet entry point. %% %% This doclet is tightly coupled with {@link edoc_layout_chunks} %% and should be used together with it. %% %% The only option this doclet accepts is `dir', which controls %% where the `chunks' subdirectory and the EEP-48 chunk files will be placed. -spec run(edoc_doclet:command(), edoc_doclet:context()) -> ok. run(#doclet_gen{} = Cmd, Ctxt) -> gen(Cmd#doclet_gen.sources, Cmd#doclet_gen.app, Cmd#doclet_gen.modules, Ctxt); run(#doclet_toc{} = _Cmd, _Ctxt) -> erlang:error(not_implemented). gen(Sources, _App, Modules, Ctxt) -> Dir = filename:join(Ctxt#doclet_context.dir, ?CHUNKS_DIR), Env = Ctxt#doclet_context.env, Options = Ctxt#doclet_context.opts, case sources(Sources, Dir, Modules, Env, Options) of {_, true = _Error} -> exit(error); {_, false} -> ok end. %% @doc Process the individual source files. %% NEW-OPTIONS: file_suffix, private, hidden %% INHERIT-OPTIONS: edoc:layout/2 %% INHERIT-OPTIONS: edoc:get_doc/3 %% DEFER-OPTIONS: run/2 sources(Sources, Dir, Modules, Env, Options) -> Suffix = proplists:get_value(file_suffix, Options, ?DEFAULT_FILE_SUFFIX), {Ms, E} = lists:foldl(fun (Src, {Set, Error}) -> source(Src, Dir, Suffix, Env, Set, Error, Options) end, {sets:new(), false}, Sources), {[M || M <- Modules, sets:is_element(M, Ms)], E}. %% @doc Write a chunk file for a source file. %% %% Add its name to the set if it was successful. %% Errors are just flagged at this stage, %% allowing all source files to be processed even if some of them fail. source({_M, Name, Path}, Dir, Suffix, Env, OkSet, ErrorFlag, Options0) -> File = filename:join(Path, Name), try %% Without these opts the entries returned by EDoc core (`edoc_extract:source1/5') won't have %% all the necessary data to generate chunks. RequiredChunkOpts = [return_entries, private, hidden], %% But we also want to have the real user-defined `private' accessible. Options = ([{show_private, proplists:get_bool(private, Options0)}] ++ RequiredChunkOpts ++ Options0), {_Module, Doc, Entries} = edoc:get_doc(File, Env, Options), Chunk = edoc:layout(Doc, [{entries, Entries}, {source, Name} | Options]), WriteOptions = [{encoding, utf8}], ok = write_file(Chunk, Dir, chunk_file_name(Name, Suffix), WriteOptions), {sets:add_element(Name, OkSet), ErrorFlag} catch _:_R:_St -> ?debug("error: ~p\n" "stacktrace:\n~p\n\n", [_R, _St]), {OkSet, true} end. chunk_file_name(ErlName, Suffix) -> string:join([filename:basename(ErlName, ".erl"), Suffix], ""). write_file(Data, Dir, Name, _Options) -> File = filename:join([Dir, Name]), ok = filelib:ensure_dir(File), case file:write_file(File, Data) of ok -> ok; {error, R} -> R1 = file:format_error(R), report("could not write file '~ts': ~ts.", [File, R1]), exit(error) end.