您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

120 行
5.0 KiB

  1. -module(rebar_prv_edoc).
  2. -behaviour(provider).
  3. -export([init/1,
  4. do/1,
  5. format_error/1]).
  6. -include("rebar.hrl").
  7. -include_lib("providers/include/providers.hrl").
  8. -define(PROVIDER, edoc).
  9. -define(DEPS, [compile]).
  10. %% ===================================================================
  11. %% Public API
  12. %% ===================================================================
  13. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  14. init(State) ->
  15. State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
  16. {module, ?MODULE},
  17. {bare, true},
  18. {deps, ?DEPS},
  19. {example, "rebar3 edoc"},
  20. {short_desc, "Generate documentation using edoc."},
  21. {desc, "Generate documentation using edoc."},
  22. {opts, []},
  23. {profiles, [docs]}])),
  24. {ok, State1}.
  25. -spec do(rebar_state:t()) ->
  26. {ok, rebar_state:t()} | {error, string()} | {error, {module(), any()}}.
  27. do(State) ->
  28. rebar_paths:set_paths([deps, plugins], State),
  29. ProjectApps = rebar_state:project_apps(State),
  30. Providers = rebar_state:providers(State),
  31. EdocOpts = rebar_state:get(State, edoc_opts, []),
  32. ShouldAccPaths = not has_configured_paths(EdocOpts),
  33. Cwd = rebar_state:dir(State),
  34. rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
  35. Res = try
  36. lists:foldl(fun(AppInfo, EdocOptsAcc) ->
  37. rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State),
  38. AppName = rebar_utils:to_list(rebar_app_info:name(AppInfo)),
  39. ?INFO("Running edoc for ~ts", [AppName]),
  40. AppDir = rebar_app_info:dir(AppInfo),
  41. AppOpts = rebar_app_info:opts(AppInfo),
  42. %% order of the merge is important to allow app opts overrides
  43. AppEdocOpts = merge_opts(rebar_opts:get(AppOpts, edoc_opts, []), EdocOptsAcc),
  44. ?DEBUG("{edoc_opts, ~p}.", [AppEdocOpts]),
  45. AppRes = (catch edoc:application(list_to_atom(AppName), AppDir, AppEdocOpts)),
  46. rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State),
  47. case {AppRes, ShouldAccPaths} of
  48. {ok, true} ->
  49. %% edoc wants / on all OSes
  50. add_to_paths(EdocOptsAcc, AppDir++"/doc");
  51. {ok, false} ->
  52. EdocOptsAcc;
  53. {{'EXIT', error}, _} ->
  54. %% EDoc is not very descriptive
  55. %% in terms of failures
  56. throw({app_failed, AppName})
  57. end
  58. end, EdocOpts, ProjectApps)
  59. catch
  60. {app_failed, AppName} ->
  61. {app_failed, AppName}
  62. end,
  63. rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State),
  64. rebar_paths:set_paths([plugins, deps], State),
  65. case Res of
  66. {app_failed, App} ->
  67. ?PRV_ERROR({app_failed, App});
  68. _ ->
  69. {ok, State}
  70. end.
  71. -spec format_error(any()) -> iolist().
  72. format_error({app_failed, AppName}) ->
  73. io_lib:format("Failed to generate documentation for app '~ts'", [AppName]);
  74. format_error(Reason) ->
  75. io_lib:format("~p", [Reason]).
  76. %% ===================================================================
  77. %% Internal functions
  78. %% ===================================================================
  79. has_configured_paths(EdocOpts) ->
  80. proplists:get_value(dir, EdocOpts) =/= undefined.
  81. add_to_paths([], Path) ->
  82. [{doc_path, [Path]}];
  83. add_to_paths([{doc_path, Paths}|T], Path) ->
  84. [{doc_path, [Path | Paths]} | T];
  85. add_to_paths([H|T], Path) ->
  86. [H | add_to_paths(T, Path)].
  87. merge_opts(AppOpts, BaseOpts) ->
  88. merge_epp_macros(rebar_utils:tup_umerge(AppOpts, BaseOpts)).
  89. %% @private the `{macros, ...}' definitions for epp can't be
  90. %% containing duplicate definitions even if multiple macro lists
  91. %% are supported, so we need to manually remove duplicates
  92. %% and merge the many lists into a single one.
  93. merge_epp_macros([]) ->
  94. [];
  95. merge_epp_macros([{macros, M1}, {macros, M2} | Rest]) ->
  96. NewMacros = dedupe_macros(lists:usort(M1), lists:usort(M2)),
  97. merge_epp_macros( [{macros, NewMacros} | Rest]);
  98. merge_epp_macros([H | T]) ->
  99. [H | merge_epp_macros(T)].
  100. dedupe_macros([], Bs) -> Bs;
  101. dedupe_macros(As, []) -> As;
  102. dedupe_macros([{K, V1} | As], [{K, _} | Bs]) ->
  103. [{K, V1} | dedupe_macros(As, Bs)];
  104. dedupe_macros([{KA, VA} | As], [{KB, VB} | Bs]) ->
  105. if KA < KB -> [{KA, VA} | dedupe_macros(As, [{KB, VB} | Bs])];
  106. KA > KB -> [{KB, VB} | dedupe_macros([{KA, VA} | As], Bs)]
  107. end.