1%% @doc EDoc command line interface 2-module(edoc_cli). 3-export([main/1]). 4 5%% TODO: accept `private'/`hidden' and forward accordingly 6 7main([]) -> 8 print(usage()); 9main(Args) -> 10 Opts = parse_args(Args), 11 print("Running with opts:\n~p\n", [Opts]), 12 ok = code:add_pathsa(maps:get(code_paths, Opts)), 13 case Opts of 14 #{run := app, app := App} -> 15 edoc:application(App, edoc_opts(Opts)); 16 #{run := files, files := Files} -> 17 edoc:files(Files, edoc_opts(Opts)) 18 end. 19 20parse_args(Args) -> 21 Init = #{mode => default, 22 run => app, 23 app => no_app, 24 files => [], 25 code_paths => [], 26 out_dir => undefined, 27 include_paths => [], 28 continue => false}, 29 check_opts(maps:without([continue], parse_args(Args, Init))). 30 31parse_args([], Opts) -> 32 Opts; 33parse_args(["-" ++ _ = Arg | Args], #{continue := Cont} = Opts) when Cont /= false -> 34 parse_args([Arg | Args], Opts#{continue := false}); 35 36parse_args(["-chunks" | Args], Opts) -> 37 parse_args(Args, Opts#{mode := chunks}); 38 39parse_args(["-o", OutDir | Args], Opts) -> 40 parse_args(Args, Opts#{out_dir := OutDir}); 41 42parse_args(["-pa", Path | Args], Opts) -> 43 #{code_paths := Paths} = Opts, 44 parse_args(Args, Opts#{code_paths := Paths ++ [Path]}); 45 46parse_args(["-I", Path | Args], Opts) -> 47 #{include_paths := Paths} = Opts, 48 parse_args(Args, Opts#{include_paths := Paths ++ [Path]}); 49 50parse_args(["-app", App | Args], Opts) -> 51 parse_args(Args, Opts#{run := app, app := list_to_atom(App)}); 52 53parse_args(["-files" | Args], Opts) -> 54 parse_args(Args, Opts#{run := files, continue := files}); 55parse_args([File | Args], #{continue := files} = Opts) -> 56 #{files := Files} = Opts, 57 parse_args(Args, Opts#{files := Files ++ [File]}); 58 59parse_args([Unknown | Args], Opts) -> 60 print("Unknown option: ~ts\n", [Unknown]), 61 parse_args(Args, Opts). 62 63check_opts(Opts) -> 64 case Opts of 65 #{run := app, app := App} when is_atom(App), App /= no_app -> ok; 66 #{run := app, app := no_app} -> quit(no_app, Opts); 67 #{run := files, files := [_|_]} -> ok; 68 #{run := files, files := []} -> quit(no_files, Opts) 69 end, 70 #{mode := Mode, 71 out_dir := OutDir, 72 code_paths := CodePaths, 73 include_paths := IncludePaths} = Opts, 74 lists:member(Mode, [default, chunks]) orelse erlang:error(mode, Opts), 75 if 76 is_list(OutDir) -> ok; 77 OutDir =:= undefined -> ok; 78 OutDir =/= undefined -> erlang:error(out_dir, Opts) 79 end, 80 is_list(CodePaths) orelse erlang:error(code_paths), 81 is_list(IncludePaths) orelse erlang:error(include_paths), 82 Opts. 83 84quit(Reason, _Opts) -> 85 case Reason of 86 no_app -> 87 print("No app name specified\n"); 88 no_files -> 89 print("No files to process\n") 90 end, 91 print("\n"), 92 print(usage()), 93 erlang:halt(1). 94 95edoc_opts(Opts) -> 96 EdocOpts = case maps:get(mode, Opts) of 97 default -> 98 [{preprocess, true}]; 99 chunks -> 100 [{doclet, edoc_doclet_chunks}, 101 {layout, edoc_layout_chunks}, 102 {preprocess, true}] 103 end, 104 OutDir = maps:get(out_dir, Opts), 105 [{includes, maps:get(include_paths, Opts)} | EdocOpts] ++ 106 [{dir, OutDir} || OutDir /= undefined]. 107 108print(Text) -> 109 print(Text, []). 110 111print(Fmt, Args) -> 112 io:format(Fmt, Args). 113 114usage() -> 115 "Usage: edoc [options] -app App\n" 116 " edoc [options] -files Source...\n" 117 "\n" 118 "Run EDoc from the command line:\n" 119 " -app App \truns edoc:application/2; App is the application name\n" 120 " -files Sources \truns edoc:files/2; Sources are .erl files\n" 121 "\n" 122 "Options:\n" 123 " -chunks \twhen present, only doc chunks are generated\n" 124 " -o Dir \tuse Dir for doc output\n" 125 " -I IncPath \tadd IncPath to EDoc include file search path;\n" 126 " \tcan be used multiple times\n" 127 " -pa CodePath \tadd CodePath to Erlang code path; can be used multiple times\n". 128