cli.ex (7061B)
1 defmodule ExDoc.CLI do 2 @moduledoc false 3 4 @doc """ 5 Handles the command line parsing for the escript. 6 """ 7 def main(args, generator \\ &ExDoc.generate_docs/3) do 8 {:ok, _} = Application.ensure_all_started(:ex_doc) 9 10 {opts, args, _invalid} = 11 OptionParser.parse(args, 12 aliases: [ 13 c: :config, 14 f: :formatter, 15 l: :logo, 16 m: :main, 17 n: :canonical, 18 o: :output, 19 p: :homepage_url, 20 q: :quiet, 21 u: :source_url, 22 v: :version 23 ], 24 switches: [ 25 formatter: :keep, 26 language: :string, 27 package: :string, 28 paths: :keep, 29 proglang: :string, 30 quiet: :boolean, 31 source_ref: :string, 32 version: :boolean 33 ] 34 ) 35 36 if List.keymember?(opts, :version, 0) do 37 print_version() 38 else 39 generate(args, opts, generator) 40 end 41 end 42 43 defp print_version do 44 IO.puts("ExDoc v#{ExDoc.version()}") 45 end 46 47 defp generate(args, opts, generator) do 48 [project, version, source_beam] = parse_args(args) 49 50 Code.prepend_path(source_beam) 51 52 for path <- Keyword.get_values(opts, :paths), 53 path <- Path.wildcard(path) do 54 Code.prepend_path(path) 55 end 56 57 opts = 58 opts 59 |> Keyword.put(:source_beam, source_beam) 60 |> Keyword.put(:apps, [app(source_beam)]) 61 |> merge_config() 62 63 quiet? = Keyword.get(opts, :quiet, false) 64 65 for formatter <- get_formatters(opts) do 66 index = generator.(project, version, Keyword.put(opts, :formatter, formatter)) 67 68 quiet? || 69 IO.puts(IO.ANSI.format([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"])) 70 71 index 72 end 73 end 74 75 defp get_formatters(opts) do 76 case Keyword.get_values(opts, :formatter) do 77 [] -> opts[:formatters] || ["html", "epub"] 78 values -> values 79 end 80 end 81 82 defp app(source_beam) do 83 case Path.wildcard(Path.join([source_beam, "*.app"])) do 84 [path] -> 85 path |> Path.basename(".app") |> String.to_atom() 86 87 _ -> 88 raise "cannot find .app file in #{inspect(source_beam)}" 89 end 90 end 91 92 defp merge_config(opts) do 93 case Keyword.fetch(opts, :config) do 94 {:ok, config} -> 95 opts 96 |> Keyword.delete(:config) 97 |> Keyword.merge(read_config(config)) 98 99 _ -> 100 opts 101 end 102 end 103 104 defp read_config(path) do 105 case Path.extname(path) do 106 ".exs" -> 107 read_config_exs(path) 108 109 ".config" -> 110 read_config_erl(path) 111 112 other -> 113 raise "expected config to have .exs or .config extension, got: #{inspect(other)}" 114 end 115 end 116 117 defp read_config_exs(path) do 118 config = File.read!(path) 119 {result, _} = Code.eval_string(config) 120 121 unless is_list(result) do 122 raise "expected a keyword list from config file: #{inspect(path)}" 123 end 124 125 result 126 end 127 128 defp read_config_erl(path) do 129 case :file.consult(path) do 130 {:ok, config} -> 131 config 132 133 {:error, reason} -> 134 raise "error parsing #{path}: #{inspect(reason)}" 135 end 136 end 137 138 defp parse_args([_project, _version, _source_beam] = args), do: args 139 140 defp parse_args([_, _, _ | _]) do 141 IO.puts("Too many arguments.\n") 142 print_usage() 143 exit({:shutdown, 1}) 144 end 145 146 defp parse_args(_) do 147 IO.puts("Too few arguments.\n") 148 print_usage() 149 exit({:shutdown, 1}) 150 end 151 152 defp print_usage do 153 IO.puts(~S""" 154 Usage: 155 ex_doc PROJECT VERSION BEAMS [OPTIONS] 156 157 Examples: 158 ex_doc "Ecto" "0.8.0" "_build/dev/lib/ecto/ebin" -u "https://github.com/elixir-ecto/ecto" 159 ex_doc "Project" "1.0.0" "_build/dev/lib/project/ebin" -c "docs.exs" 160 161 Options: 162 PROJECT Project name 163 VERSION Version number 164 BEAMS Path to compiled beam files 165 -n, --canonical Indicate the preferred URL with rel="canonical" link element 166 -c, --config Give configuration through a file instead of a command line. 167 See "Custom config" section below for more information. 168 -f, --formatter Docs formatter to use (html or epub), default: html and epub 169 -p, --homepage-url URL to link to for the site name 170 --language Identify the primary language of the documents, its value must be 171 a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag, default: "en" 172 -l, --logo Path to the image logo of the project (only PNG or JPEG accepted) 173 The image size will be 64x64 and copied to the assets directory 174 -m, --main The entry-point page in docs, default: "api-reference" 175 -o, --output Path to output docs, default: "doc" 176 --package Hex package name 177 --paths Prepends the given path to Erlang code path. The path might contain a glob 178 pattern but in that case, remember to quote it: --paths "_build/dev/lib/*/ebin". 179 This option can be given multiple times 180 --proglang The project's programming language, default: "elixir" 181 -q, --quiet Only output warnings and errors 182 --source-ref Branch/commit/tag used for source link inference, default: "master" 183 -u, --source-url URL to the source code 184 -v, --version Print ExDoc version 185 186 ## Custom config 187 188 A custom config can be given with the `--config` option. 189 190 The file must either have ".exs" or ".config" extension. 191 192 The file with the ".exs" extension must be an Elixir script that returns a keyword list with 193 the same options declares in `Mix.Tasks.Docs`. Here is an example: 194 195 [ 196 extras: Path.wildcard("lib/elixir/pages/*.md"), 197 groups_for_functions: [ 198 Guards: & &1[:guard] == true 199 ], 200 skip_undefined_reference_warnings_on: ["compatibility-and-deprecations"], 201 groups_for_modules: [ 202 ... 203 ] 204 ] 205 206 The file with the ".config" extension must contain Erlang terms separated by ".". 207 See `:file.consult/1` for more information. Here is an example: 208 209 {extras, [<<"README.md">>, <<"CHANGELOG.md">>]}. 210 {main, <<"readme">>}. 211 {proglang, erlang}. 212 213 ## Source linking 214 215 ExDoc by default provides links to the source code implementation as 216 long as `--source-url` or `--source-url-pattern` is provided. If you 217 provide `--source-url`, ExDoc will inflect the url pattern automatically 218 for GitHub, GitLab, and Bitbucket URLs. For example: 219 220 --source-url "https://github.com/elixir-ecto/ecto" 221 222 Will be inflected as: 223 224 https://github.com/elixir-ecto/ecto/blob/master/%{path}#L%{line} 225 226 To specify a particular branch or commit, use the `--source-ref` option: 227 228 --source-url "https://github.com/elixir-ecto/ecto" --source-ref "v1.0" 229 230 will result in the following URL pattern: 231 232 https://github.com/elixir-ecto/ecto/blob/v1.0/%{path}#L%{line} 233 234 """) 235 end 236 end